|
#if XUNIT_NULLABLE
#nullable enable
#endif
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Xunit.Sdk;
#if XUNIT_VALUETASK
using System.Threading.Tasks;
#endif
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<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<T>(
IEnumerable<T> collection,
Action<T, int> action)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(action), action);
#if XUNIT_NULLABLE
var errors = new Stack<Tuple<int, object?, Exception>>();
#else
var errors = new Stack<Tuple<int, object, Exception>>();
#endif
var idx = 0;
foreach (var item in collection)
{
try
{
action(item, idx);
}
catch (Exception ex)
{
#if XUNIT_NULLABLE
errors.Push(new Tuple<int, object?, Exception>(idx, item, ex));
#else
errors.Push(new Tuple<int, object, Exception>(idx, item, ex));
#endif
}
++idx;
}
if (errors.Count > 0)
throw new AllException(idx, errors.ToArray());
}
#if XUNIT_VALUETASK
/// <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 ValueTask AllAsync<T>(
IEnumerable<T> collection,
Func<T, ValueTask> 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 ValueTask AllAsync<T>(
IEnumerable<T> collection,
Func<T, int, ValueTask> action)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(action), action);
#if XUNIT_NULLABLE
var errors = new Stack<Tuple<int, object?, Exception>>();
#else
var errors = new Stack<Tuple<int, object, Exception>>();
#endif
var idx = 0;
foreach (var item in collection)
{
try
{
await action(item, idx);
}
catch (Exception ex)
{
#if XUNIT_NULLABLE
errors.Push(new Tuple<int, object?, Exception>(idx, item, ex));
#else
errors.Push(new Tuple<int, object, Exception>(idx, item, ex));
#endif
}
++idx;
}
if (errors.Count > 0)
throw new AllException(idx, errors.ToArray());
}
#endif
/// <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<T>(
IEnumerable<T> collection,
params Action<T>[] elementInspectors)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(elementInspectors), elementInspectors);
var elements = collection.ToArray();
var expectedCount = elementInspectors.Length;
var actualCount = elements.Length;
if (expectedCount != actualCount)
throw new CollectionException(collection, expectedCount, actualCount);
for (var idx = 0; idx < actualCount; idx++)
{
try
{
elementInspectors[idx](elements[idx]);
}
catch (Exception ex)
{
throw new CollectionException(collection, expectedCount, actualCount, idx, ex);
}
}
}
#if XUNIT_VALUETASK
/// <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 ValueTask CollectionAsync<T>(
IEnumerable<T> collection,
params Func<T, ValueTask>[] elementInspectors)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(elementInspectors), elementInspectors);
var elements = collection.ToArray();
var expectedCount = elementInspectors.Length;
var actualCount = elements.Length;
if (expectedCount != actualCount)
throw new CollectionException(collection, expectedCount, actualCount);
for (var idx = 0; idx < actualCount; idx++)
try
{
await elementInspectors[idx](elements[idx]);
}
catch (Exception ex)
{
throw new CollectionException(collection, expectedCount, actualCount, idx, ex);
}
}
#endif
/// <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)] T>(
T expected,
IEnumerable<T> collection)
{
GuardArgumentNotNull(nameof(collection), collection);
// If an equality comparer is not explicitly provided, call into ICollection<T>.Contains or
// IReadOnlyCollection<T>.Contains which may use the collection's equality comparer for types
// like HashSet and Dictionary. HashSet and Dictionary are normally handled explicitly, but
// the developer may end up in the IEnumerable<> override because the variable is not an explicit
// enough type.
var readWriteCollection = collection as ICollection<T>;
if (readWriteCollection != null)
{
if (readWriteCollection.Contains(expected))
return;
}
else
{
var readOnlyCollection = collection as IReadOnlyCollection<T>;
if (readOnlyCollection != null && readOnlyCollection.Contains(expected))
return;
}
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<T>(
T expected,
IEnumerable<T> collection,
IEqualityComparer<T> comparer)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(comparer), comparer);
if (collection.Contains(expected, comparer))
return;
throw new ContainsException(expected, collection);
}
/// <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<T>(
IEnumerable<T> collection,
Predicate<T> filter)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(filter), filter);
foreach (var item in collection)
if (filter(item))
return;
throw new ContainsException("(filter expression)", collection);
}
/// <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="ContainsDuplicateException">Thrown when an object is present inside the container more than once</exception>
public static void Distinct<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="ContainsDuplicateException">Thrown when an object is present inside the container more than once</exception>
public static void Distinct<T>(
IEnumerable<T> collection,
IEqualityComparer<T> comparer)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(comparer), comparer);
var set = new HashSet<T>(comparer);
foreach (var x in collection)
if (!set.Add(x))
throw new ContainsDuplicateException(x, collection);
}
/// <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)] T>(
T expected,
IEnumerable<T> collection)
{
GuardArgumentNotNull(nameof(collection), collection);
// If an equality comparer is not explicitly provided, call into ICollection<T>.Contains or
// IReadOnlyCollection<T>.Contains which may use the collection's equality comparer for types
// like HashSet and Dictionary. HashSet and Dictionary are normally handled explicitly, but
// the developer may end up in the IEnumerable<> override because the variable is not an explicit
// enough type.
var readWriteCollection = collection as ICollection<T>;
if (readWriteCollection != null)
{
if (readWriteCollection.Contains(expected))
throw new DoesNotContainException(expected, collection);
}
else
{
var readOnlyCollection = collection as IReadOnlyCollection<T>;
if (readOnlyCollection != null && readOnlyCollection.Contains(expected))
throw new DoesNotContainException(expected, collection);
}
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 container</exception>
public static void DoesNotContain<T>(
T expected,
IEnumerable<T> collection,
IEqualityComparer<T> comparer)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(comparer), comparer);
if (!collection.Contains(expected, comparer))
return;
throw new DoesNotContainException(expected, collection);
}
/// <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 container</exception>
public static void DoesNotContain<T>(
IEnumerable<T> collection,
Predicate<T> filter)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(filter), filter);
foreach (var item in collection)
if (filter(item))
throw new DoesNotContainException("(filter expression)", collection);
}
/// <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);
var enumerator = collection.GetEnumerator();
try
{
if (enumerator.MoveNext())
throw new EmptyException(collection);
}
finally
{
(enumerator as IDisposable)?.Dispose();
}
}
/// <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<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<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 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 new NotEmptyException();
}
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<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<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 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<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<T>(
IEnumerable<T> collection,
Predicate<T> predicate)
{
GuardArgumentNotNull(nameof(collection), collection);
GuardArgumentNotNull(nameof(predicate), predicate);
return GetSingleResult(collection, predicate, "(filter expression)");
}
static T GetSingleResult<T>(
IEnumerable<T> collection,
#if XUNIT_NULLABLE
Predicate<T>? predicate,
string? expectedArgument)
#else
Predicate<T> predicate,
string expectedArgument)
#endif
{
var count = 0;
var result = default(T);
foreach (var item in collection)
if (predicate == null || predicate(item))
if (++count == 1)
result = item;
switch (count)
{
case 0:
throw SingleException.Empty(expectedArgument);
case 1:
#if XUNIT_NULLABLE
return result!;
#else
return result;
#endif
default:
throw SingleException.MoreThanOne(count, expectedArgument);
}
}
}
}
|