File: CollectionAssertsTests.cs
Web Access
Project: src\src\Microsoft.DotNet.XUnitAssert\tests\Microsoft.DotNet.XUnitAssert.Tests.csproj (Microsoft.DotNet.XUnitAssert.Tests)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NSubstitute;
using Xunit;
using Xunit.Sdk;
 
public class CollectionAssertsTests
{
	public class All
	{
		[Fact]
		public static void GuardClauses()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.All(default(IEnumerable<object>)!, _ => { }));
			Assert.Throws<ArgumentNullException>("action", () => Assert.All(new object[0], (Action<object>)null!));
			Assert.Throws<ArgumentNullException>("action", () => Assert.All(new object[0], (Action<object, int>)null!));
		}
 
		[Fact]
		public static void Success()
		{
			var items = new[] { 1, 1, 1, 1, 1, 1 };
 
			Assert.All(items, x => Assert.Equal(1, x));
		}
 
		[Fact]
		public static void Failure()
		{
			var items = new[] { 1, 1, 42, 2112, 1, 1 };
 
			var ex = Record.Exception(() => Assert.All(items, item => Assert.Equal(1, item)));
 
			Assert.IsType<AllException>(ex);
			Assert.Equal(
				"Assert.All() Failure: 2 out of 6 items in the collection did not pass." + Environment.NewLine +
				"[2]: Item:  42" + Environment.NewLine +
				"     Error: Assert.Equal() Failure: Values differ" + Environment.NewLine +
				"            Expected: 1" + Environment.NewLine +
				"            Actual:   42" + Environment.NewLine +
				"[3]: Item:  2112" + Environment.NewLine +
				"     Error: Assert.Equal() Failure: Values differ" + Environment.NewLine +
				"            Expected: 1" + Environment.NewLine +
				"            Actual:   2112",
				ex.Message
			);
		}
 
		[Fact]
		public static void ActionCanReceiveIndex()
		{
			var items = new[] { 1, 1, 2, 2, 1, 1 };
			var indices = new List<int>();
 
			Assert.All(items, (_, idx) => indices.Add(idx));
 
			Assert.Equal(new[] { 0, 1, 2, 3, 4, 5 }, indices);
		}
	}
 
	public class AllAsync
	{
		[Fact]
		public static async Task GuardClauses()
		{
			await Assert.ThrowsAsync<ArgumentNullException>("collection", () => Assert.AllAsync(default(IEnumerable<object>)!, async _ => await Task.Yield()));
			await Assert.ThrowsAsync<ArgumentNullException>("action", () => Assert.AllAsync(new object[0], (Func<object, Task>)null!));
			await Assert.ThrowsAsync<ArgumentNullException>("action", () => Assert.AllAsync(new object[0], (Func<object, int, Task>)null!));
		}
 
		[Fact]
		public static async Task Success()
		{
			var items = new[] { 1, 1, 1, 1, 1, 1 };
 
			await Assert.AllAsync(items, async item => { await Task.Yield(); Assert.Equal(1, item); });
		}
 
		[Fact]
		public static void Failure()
		{
			var items = new[] { 1, 1, 42, 2112, 1, 1 };
 
			var ex = Record.Exception(() => Assert.All(items, x => Assert.Equal(1, x)));
 
			Assert.IsType<AllException>(ex);
			Assert.Equal(
				"Assert.All() Failure: 2 out of 6 items in the collection did not pass." + Environment.NewLine +
				"[2]: Item:  42" + Environment.NewLine +
				"     Error: Assert.Equal() Failure: Values differ" + Environment.NewLine +
				"            Expected: 1" + Environment.NewLine +
				"            Actual:   42" + Environment.NewLine +
				"[3]: Item:  2112" + Environment.NewLine +
				"     Error: Assert.Equal() Failure: Values differ" + Environment.NewLine +
				"            Expected: 1" + Environment.NewLine +
				"            Actual:   2112",
				ex.Message
			);
		}
 
		[Fact]
		public static async Task ActionCanReceiveIndex()
		{
			var items = new[] { 1, 1, 2, 2, 1, 1 };
			var indices = new List<int>();
 
			await Assert.AllAsync(items, async (_, idx) => { await Task.Yield(); indices.Add(idx); });
 
			Assert.Equal(new[] { 0, 1, 2, 3, 4, 5 }, indices);
		}
	}
 
	public class Collection
	{
		[Fact]
		public static void EmptyCollection()
		{
			var list = new List<int>();
 
#pragma warning disable xUnit2011 // Do not use empty collection check
			Assert.Collection(list);
#pragma warning restore xUnit2011 // Do not use empty collection check
		}
 
		[Fact]
		public static void MismatchedElementCount()
		{
			var list = new List<int>();
 
			var ex = Record.Exception(
				() => Assert.Collection(list,
					item => Assert.True(false)
				)
			);
 
			var collEx = Assert.IsType<CollectionException>(ex);
			Assert.Equal(
				"Assert.Collection() Failure: Mismatched item count" + Environment.NewLine +
				"Collection:     []" + Environment.NewLine +
				"Expected count: 1" + Environment.NewLine +
				"Actual count:   0",
				collEx.Message
			);
			Assert.Null(collEx.InnerException);
		}
 
		[Fact]
		public static void NonEmptyCollection()
		{
			var list = new List<int> { 42, 2112 };
 
			Assert.Collection(list,
				item => Assert.Equal(42, item),
				item => Assert.Equal(2112, item)
			);
		}
 
#if !NETCOREAPP2_0 // Unclear why this is failing only on .NET Core 2.0, but it passes with .NET 6 and .NET Framework 4.x
		[Fact]
		public static void MismatchedElement()
		{
			var list = new List<int> { 42, 2112 };
 
			var ex = Record.Exception(() =>
				Assert.Collection(list,
					item => Assert.Equal(42, item),
					item => Assert.Equal(2113, item)
				)
			);
 
			var collEx = Assert.IsType<CollectionException>(ex);
			Assert.StartsWith(
				"Assert.Collection() Failure: Item comparison failure" + Environment.NewLine +
				"                 ↓ (pos 1)" + Environment.NewLine +
				"Collection: [42, 2112]" + Environment.NewLine +
				"Error:      Assert.Equal() Failure: Values differ" + Environment.NewLine +
				"            Expected: 2113" + Environment.NewLine +
				"            Actual:   2112" + Environment.NewLine +
				"            Stack Trace:",
				ex.Message
			);
		}
#endif
	}
 
	public class CollectionAsync
	{
		[Fact]
		public static async Task EmptyCollection()
		{
			var list = new List<int>();
 
#pragma warning disable xUnit2011 // Do not use empty collection check
			await Assert.CollectionAsync(list);
#pragma warning restore xUnit2011 // Do not use empty collection check
		}
 
		[Fact]
		public static async Task MismatchedElementCountAsync()
		{
			var list = new List<int>();
 
			var ex = await Record.ExceptionAsync(
				() => Assert.CollectionAsync(list,
					async item => await Task.Yield()
				)
			);
 
			var collEx = Assert.IsType<CollectionException>(ex);
			Assert.Equal(
				"Assert.Collection() Failure: Mismatched item count" + Environment.NewLine +
				"Collection:     []" + Environment.NewLine +
				"Expected count: 1" + Environment.NewLine +
				"Actual count:   0",
				collEx.Message
			);
			Assert.Null(collEx.InnerException);
		}
 
		[Fact]
		public static async Task NonEmptyCollectionAsync()
		{
			var list = new List<int> { 42, 2112 };
 
			await Assert.CollectionAsync(list,
				async item =>
				{
					await Task.Yield();
					Assert.Equal(42, item);
				},
				async item =>
				{
					await Task.Yield();
					Assert.Equal(2112, item);
				}
			);
		}
 
		[Fact]
		public static async Task MismatchedElementAsync()
		{
			var list = new List<int> { 42, 2112 };
 
			var ex = await Record.ExceptionAsync(() =>
				 Assert.CollectionAsync(list,
					 async item =>
					 {
						 await Task.Yield();
						 Assert.Equal(42, item);
					 },
					 async item =>
					 {
						 await Task.Yield();
						 Assert.Equal(2113, item);
					 }
				 )
			);
 
			var collEx = Assert.IsType<CollectionException>(ex);
			Assert.StartsWith(
				"Assert.Collection() Failure: Item comparison failure" + Environment.NewLine +
				"                 ↓ (pos 1)" + Environment.NewLine +
				"Collection: [42, 2112]" + Environment.NewLine +
				"Error:      Assert.Equal() Failure: Values differ" + Environment.NewLine +
				"            Expected: 2113" + Environment.NewLine +
				"            Actual:   2112" + Environment.NewLine +
				"            Stack Trace:",
				ex.Message
			);
		}
	}
 
	public class Contains
	{
		[Fact]
		public static void GuardClause()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.Contains(14, default(IEnumerable<int>)!));
		}
 
		[Fact]
		public static void CanFindNullInContainer()
		{
			var list = new List<object?> { 16, null, "Hi there" };
 
			Assert.Contains(null, list);
		}
 
		[Fact]
		public static void ItemInContainer()
		{
			var list = new List<int> { 42 };
 
			Assert.Contains(42, list);
		}
 
		[Fact]
		public static void ItemNotInContainer()
		{
			var list = new List<int> { 41, 43 };
 
			var ex = Record.Exception(() => Assert.Contains(42, list));
 
			Assert.IsType<ContainsException>(ex);
			Assert.Equal(
				"Assert.Contains() Failure: Item not found in collection" + Environment.NewLine +
				"Collection: [41, 43]" + Environment.NewLine +
				"Not found:  42",
				ex.Message
			);
		}
 
		[Fact]
		public static void NullsAllowedInContainer()
		{
			var list = new List<object?> { null, 16, "Hi there" };
 
			Assert.Contains("Hi there", list);
		}
 
		[Fact]
		public static void SetsAreTreatedSpecially()
		{
			IEnumerable<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Hi there" };
 
			Assert.Contains("HI THERE", set);
		}
 
#if NET5_0_OR_GREATER
		[Fact]
		public static void ReadOnlySetsAreTreatedSpecially()
		{
			IEnumerable<string> set = new ReadOnlySet<string>(StringComparer.OrdinalIgnoreCase, "Hi there");
 
			Assert.Contains("HI THERE", set);
		}
#endif
	}
 
	public class Contains_Comparer
	{
		[Fact]
		public static void GuardClauses()
		{
			var comparer = Substitute.For<IEqualityComparer<int>>();
 
			Assert.Throws<ArgumentNullException>("collection", () => Assert.Contains(14, default(IEnumerable<int>)!, comparer));
			Assert.Throws<ArgumentNullException>("comparer", () => Assert.Contains(14, new int[0], null!));
		}
 
		[Fact]
		public static void CanUseComparer()
		{
			var list = new List<int> { 42 };
 
			Assert.Contains(43, list, new MyComparer());
		}
 
		[Fact]
		public static void HashSetConstructorComparerIsIgnored()
		{
			IEnumerable<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Hi there" };
 
			var ex = Record.Exception(() => Assert.Contains("HI THERE", set, StringComparer.Ordinal));
 
			Assert.IsType<ContainsException>(ex);
			Assert.Equal(
				"Assert.Contains() Failure: Item not found in collection" + Environment.NewLine +
				"Collection: [\"Hi there\"]" + Environment.NewLine +
				"Not found:  \"HI THERE\"",
				ex.Message
			);
		}
 
		class MyComparer : IEqualityComparer<int>
		{
			public bool Equals(int x, int y) => true;
 
			public int GetHashCode(int obj) => throw new NotImplementedException();
		}
	}
 
	public class Contains_Predicate
	{
		[Fact]
		public static void GuardClauses()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.Contains(default(IEnumerable<int>)!, item => true));
			Assert.Throws<ArgumentNullException>("filter", () => Assert.Contains(new int[0], (Predicate<int>)null!));
		}
 
		[Fact]
		public static void ItemFound()
		{
			var list = new[] { "Hello", "world" };
 
			Assert.Contains(list, item => item.StartsWith("w"));
		}
 
		[Fact]
		public static void ItemNotFound()
		{
			var list = new[] { "Hello", "world" };
 
			var ex = Record.Exception(() => Assert.Contains(list, item => item.StartsWith("q")));
 
			Assert.IsType<ContainsException>(ex);
			Assert.Equal(
				"Assert.Contains() Failure: Filter not matched in collection" + Environment.NewLine +
				"Collection: [\"Hello\", \"world\"]",
				ex.Message
			);
		}
	}
 
	public class Distinct
	{
		[Fact]
		public static void GuardClauses()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.Distinct(default(IEnumerable<int>)!));
			Assert.Throws<ArgumentNullException>("comparer", () => Assert.Distinct(new object[0], null!));
		}
 
		[Fact]
		public static void WithNull()
		{
			var list = new List<object?> { 16, "Hi there", null };
 
			Assert.Distinct(list);
		}
 
		[Fact]
		public static void TwoItems()
		{
			var list = new List<int> { 42, 42 };
 
			var ex = Record.Exception(() => Assert.Distinct(list));
 
			Assert.IsType<DistinctException>(ex);
			Assert.Equal(
				"Assert.Distinct() Failure: Duplicate item found" + Environment.NewLine +
				"Collection: [42, 42]" + Environment.NewLine +
				"Item:       42",
				ex.Message
			);
		}
 
		[Fact]
		public static void TwoNulls()
		{
			var list = new List<string?> { "a", null, "b", null, "c", "d" };
 
			var ex = Record.Exception(() => Assert.Distinct(list));
 
			Assert.IsType<DistinctException>(ex);
			Assert.Equal(
				"Assert.Distinct() Failure: Duplicate item found" + Environment.NewLine +
				$"Collection: [\"a\", null, \"b\", null, \"c\", {ArgumentFormatter.Ellipsis}]" + Environment.NewLine +
				"Item:       null",
				ex.Message
			);
		}
 
		[Fact]
		public static void CaseSensitiveStrings()
		{
			var list = new string[] { "a", "b", "A" };
 
			Assert.Distinct(list);
			Assert.Distinct(list, StringComparer.Ordinal);
		}
 
		[Fact]
		public static void CaseInsensitiveStrings()
		{
			var list = new string[] { "a", "b", "A" };
 
			var ex = Record.Exception(() => Assert.Distinct(list, StringComparer.OrdinalIgnoreCase));
 
			Assert.IsType<DistinctException>(ex);
			Assert.Equal(
				"Assert.Distinct() Failure: Duplicate item found" + Environment.NewLine +
				"Collection: [\"a\", \"b\", \"A\"]" + Environment.NewLine +
				"Item:       \"A\"",
				ex.Message
			);
		}
	}
 
	public class DoesNotContain
	{
		[Fact]
		public static void GuardClause()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.DoesNotContain(14, default(IEnumerable<int>)!));
		}
 
		[Fact]
		public static void CanSearchForNullInContainer()
		{
			var list = new List<object?> { 16, "Hi there" };
 
			Assert.DoesNotContain(null, list);
		}
 
		[Fact]
		public static void ItemInContainer()
		{
			var list = new List<int> { 42 };
 
			var ex = Record.Exception(() => Assert.DoesNotContain(42, list));
 
			Assert.IsType<DoesNotContainException>(ex);
			Assert.Equal(
				"Assert.DoesNotContain() Failure: Item found in collection" + Environment.NewLine +
				"             ↓ (pos 0)" + Environment.NewLine +
				"Collection: [42]" + Environment.NewLine +
				"Found:      42",
				ex.Message
			);
		}
 
		[Fact]
		public static void ItemNotInContainer()
		{
			var list = new List<int>();
 
			Assert.DoesNotContain(42, list);
		}
 
		[Fact]
		public static void NullsAllowedInContainer()
		{
			var list = new List<object?> { null, 16, "Hi there" };
 
			Assert.DoesNotContain(42, list);
		}
 
		[Fact]
		public static void SetsAreTreatedSpecially()
		{
			IEnumerable<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Hi there" };
 
			var ex = Record.Exception(() => Assert.DoesNotContain("HI THERE", set));
 
			Assert.IsType<DoesNotContainException>(ex);
			// Note: There is no pointer for sets, unlike other collections
			Assert.Equal(
				"Assert.DoesNotContain() Failure: Item found in set" + Environment.NewLine +
				"Set:   [\"Hi there\"]" + Environment.NewLine +
				"Found: \"HI THERE\"",
				ex.Message
			);
		}
 
#if NET5_0_OR_GREATER
		[Fact]
		public static void ReadOnlySetsAreTreatedSpecially()
		{
			IEnumerable<string> set = new ReadOnlySet<string>(StringComparer.OrdinalIgnoreCase, "Hi there");
 
			var ex = Record.Exception(() => Assert.DoesNotContain("HI THERE", set));
 
			Assert.IsType<DoesNotContainException>(ex);
			// Note: There is no pointer for sets, unlike other collections
			Assert.Equal(
				"Assert.DoesNotContain() Failure: Item found in set" + Environment.NewLine +
				"Set:   [\"Hi there\"]" + Environment.NewLine +
				"Found: \"HI THERE\"",
				ex.Message
			);
		}
#endif
	}
 
	public class DoesNotContain_Comparer
	{
		[Fact]
		public static void GuardClauses()
		{
			var comparer = Substitute.For<IEqualityComparer<int>>();
 
			Assert.Throws<ArgumentNullException>("collection", () => Assert.DoesNotContain(14, default(IEnumerable<int>)!, comparer));
			Assert.Throws<ArgumentNullException>("comparer", () => Assert.DoesNotContain(14, new int[0], null!));
		}
 
		[Fact]
		public static void CanUseComparer()
		{
			var list = new List<int> { 42 };
 
			Assert.DoesNotContain(42, list, new MyComparer());
		}
 
		[Fact]
		public static void HashSetConstructorComparerIsIgnored()
		{
			IEnumerable<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Hi there" };
 
			Assert.DoesNotContain("HI THERE", set, StringComparer.Ordinal);
		}
 
		class MyComparer : IEqualityComparer<int>
		{
			public bool Equals(int x, int y) => false;
 
			public int GetHashCode(int obj) => throw new NotImplementedException();
		}
	}
 
	public class DoesNotContain_Predicate
	{
		[Fact]
		public static void GuardClauses()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.DoesNotContain(default(IEnumerable<int>)!, item => true));
			Assert.Throws<ArgumentNullException>("filter", () => Assert.DoesNotContain(new int[0], (Predicate<int>)null!));
		}
 
		[Fact]
		public static void ItemFound()
		{
			var list = new[] { "Hello", "world" };
 
			var ex = Record.Exception(() => Assert.DoesNotContain(list, item => item.StartsWith("w")));
 
			Assert.IsType<DoesNotContainException>(ex);
			Assert.Equal(
				"Assert.DoesNotContain() Failure: Filter matched in collection" + Environment.NewLine +
				"                      ↓ (pos 1)" + Environment.NewLine +
				"Collection: [\"Hello\", \"world\"]",
				ex.Message
			);
		}
 
		[Fact]
		public static void ItemNotFound()
		{
			var list = new[] { "Hello", "world" };
 
			Assert.DoesNotContain(list, item => item.StartsWith("q"));
		}
	}
 
	public class Empty
	{
		[Fact]
		public static void GuardClause()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.Empty(default(IEnumerable)!));
		}
 
		[Fact]
		public static void EmptyCollection()
		{
			var list = new List<int>();
 
			Assert.Empty(list);
		}
 
		[Fact]
		public static void NonEmptyCollection()
		{
			var list = new List<int> { 42 };
 
			var ex = Record.Exception(() => Assert.Empty(list));
 
			Assert.IsType<EmptyException>(ex);
			Assert.Equal(
				"Assert.Empty() Failure: Collection was not empty" + Environment.NewLine +
				"Collection: [42]",
				ex.Message
			);
		}
 
		[Fact]
		public static void CollectionEnumeratorDisposed()
		{
			var enumerator = new SpyEnumerator<int>(Enumerable.Empty<int>());
 
			Assert.Empty(enumerator);
 
			Assert.True(enumerator.IsDisposed);
		}
	}
 
	public class Equal
	{
		public class Null
		{
			[Fact]
			public static void BothNull()
			{
				var expected = default(IEnumerable<int>);
				var actual = default(IEnumerable<int>);
 
				Assert.Equal(expected, actual);
			}
 
			[Fact]
			public static void EmptyExpectedNullActual()
			{
				var expected = new int[0];
				var actual = default(IEnumerable<int>);
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Collections differ" + Environment.NewLine +
					"Expected: int[] []" + Environment.NewLine +
					"Actual:         null",
					ex.Message
				);
			}
 
			[Fact]
			public static void NullExpectedEmptyActual()
			{
				var expected = default(IEnumerable<int>);
				var actual = new int[0];
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Collections differ" + Environment.NewLine +
					"Expected:       null" + Environment.NewLine +
					"Actual:   int[] []",
					ex.Message
				);
			}
		}
 
		public class Arrays
		{
			[Fact]
			public static void Equal()
			{
				string[] expected = { "@", "a", "ab", "b" };
				string[] actual = { "@", "a", "ab", "b" };
 
				Assert.Equal(expected, actual);
			}
 
			[Fact]
			public static void EmbeddedArrays_Equal()
			{
				string[][] expected = { new[] { "@", "a" }, new[] { "ab", "b" } };
				string[][] actual = { new[] { "@", "a" }, new[] { "ab", "b" } };
 
				Assert.Equal(expected, actual);
			}
 
			[Theory]
			// Nulls
			[InlineData(null, new[] { 1, 2, 3 }, null, null)]
			[InlineData(new[] { 1, 2, 3 }, null, null, null)]
			// Difference at start
			[InlineData(new[] { 0, 2, 3, 4 }, new[] { 1, 2, 3, 4 }, "↓ (pos 0)", "↑ (pos 0)")]
			// Inline difference
			[InlineData(new[] { 1, 0, 3, 4 }, new[] { 1, 2, 3, 4 }, "   ↓ (pos 1)", "   ↑ (pos 1)")]
			// Difference at end
			[InlineData(new[] { 1, 2, 3, 0 }, new[] { 1, 2, 3, 4 }, "         ↓ (pos 3)", "         ↑ (pos 3)")]
			// Overruns
			[InlineData(new[] { 1, 2, 3, 4 }, new[] { 1, 2, 3, 4, 5 }, null, "            ↑ (pos 4)")]
			[InlineData(new[] { 1, 2, 3, 4, 5 }, new[] { 1, 2, 3, 4 }, "            ↓ (pos 4)", null)]
			[InlineData(new[] { 1 }, new int[0], "↓ (pos 0)", null)]
			[InlineData(new int[0], new[] { 1 }, null, "↑ (pos 0)")]
			public void NotEqual(
				int[]? expected,
				int[]? actual,
				string? expectedPointer,
				string? actualPointer)
			{
				string message = "Assert.Equal() Failure: Collections differ";
 
				if (expectedPointer is not null)
					message += Environment.NewLine + "           " + expectedPointer;
 
				var (expectedType, actualType) = (expected, actual) switch
				{
					(null, _) => ("      ", "int[] "),
					(_, null) => ("int[] ", "      "),
					(_, _) => ("", ""),
				};
 
				message +=
					Environment.NewLine + "Expected: " + expectedType + ArgumentFormatter.Format(expected) +
					Environment.NewLine + "Actual:   " + actualType + ArgumentFormatter.Format(actual);
 
				if (actualPointer is not null)
					message += Environment.NewLine + "           " + actualPointer;
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(message, ex.Message);
			}
 
			[Theory]
			// Nulls
			[InlineData(
				null, null, "      null",
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "int[] [1, 2, 3, 4, 5, $$ELLIPSIS$$]", null
			)]
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, null, "int[] [1, 2, 3, 4, 5, $$ELLIPSIS$$]",
				null, "      null", null
			)]
			// Start of array
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "↓ (pos 0)", "[1, 2, 3, 4, 5, $$ELLIPSIS$$]",
				new[] { 99, 2, 3, 4, 5, 6, 7 }, "[99, 2, 3, 4, 5, $$ELLIPSIS$$]", "↑ (pos 0)"
			)]
			// Middle of array
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "           ↓ (pos 3)", "[$$ELLIPSIS$$, 2, 3, 4, 5, 6, $$ELLIPSIS$$]",
				new[] { 1, 2, 3, 99, 5, 6, 7 }, "[$$ELLIPSIS$$, 2, 3, 99, 5, 6, $$ELLIPSIS$$]", "           ↑ (pos 3)"
			)]
			// End of array
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "                 ↓ (pos 6)", "[$$ELLIPSIS$$, 3, 4, 5, 6, 7]",
				new[] { 1, 2, 3, 4, 5, 6, 99 }, "[$$ELLIPSIS$$, 3, 4, 5, 6, 99]", "                 ↑ (pos 6)"
			)]
			// Overruns
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, null, "[$$ELLIPSIS$$, 4, 5, 6, 7]",
				new[] { 1, 2, 3, 4, 5, 6, 7, 8 }, "[$$ELLIPSIS$$, 4, 5, 6, 7, 8]", "                 ↑ (pos 7)"
			)]
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, null, "[$$ELLIPSIS$$, 6, 7]",
				new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, "[$$ELLIPSIS$$, 6, 7, 8, 9, 10, $$ELLIPSIS$$]", "           ↑ (pos 7)"
			)]
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7, 8 }, "                 ↓ (pos 7)", "[$$ELLIPSIS$$, 4, 5, 6, 7, 8]",
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "[$$ELLIPSIS$$, 4, 5, 6, 7]", null
			)]
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, "           ↓ (pos 7)", "[$$ELLIPSIS$$, 6, 7, 8, 9, 10, $$ELLIPSIS$$]",
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "[$$ELLIPSIS$$, 6, 7]", null
			)]
			public void Truncation(
				int[]? expected,
				string? expectedPointer,
				string expectedDisplay,
				int[]? actual,
				string actualDisplay,
				string? actualPointer)
			{
				var message = "Assert.Equal() Failure: Collections differ";
 
				if (expectedPointer is not null)
					message += Environment.NewLine + "           " + expectedPointer;
 
				message +=
					Environment.NewLine + "Expected: " + expectedDisplay +
					Environment.NewLine + "Actual:   " + actualDisplay;
 
				if (actualPointer is not null)
					message += Environment.NewLine + "           " + actualPointer;
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(message.Replace("$$ELLIPSIS$$", ArgumentFormatter.Ellipsis), ex.Message);
			}
 
			[Fact]
			public void SameValueDifferentType()
			{
				var ex = Record.Exception(() => Assert.Equal(new object[] { 1, 2, 3 }, new object[] { 1, 2, 3L }));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Collections differ" + Environment.NewLine +
					"                 ↓ (pos 2, type System.Int32)" + Environment.NewLine +
					"Expected: [1, 2, 3]" + Environment.NewLine +
					"Actual:   [1, 2, 3]" + Environment.NewLine +
					"                 ↑ (pos 2, type System.Int64)",
					ex.Message
				);
			}
		}
 
		public class ArraysWithComparer
		{
			// https://github.com/xunit/xunit/issues/2795
			[Fact]
			public void CollectionItemIsEnumerable()
			{
				var actual = new EnumerableItem[] { new(0), new(2) };
				var expected = new EnumerableItem[] { new(1), new(3) };
 
				Assert.Equal(expected, actual, new EnumerableItemComparer());
			}
 
			public class EnumerableItemComparer : IEqualityComparer<EnumerableItem>
			{
				public bool Equals(EnumerableItem? x, EnumerableItem? y) =>
					x?.Value / 2 == y?.Value / 2;
 
				public int GetHashCode(EnumerableItem obj) =>
					throw new NotImplementedException();
			}
 
			public sealed class EnumerableItem : IEnumerable<string>
			{
				public int Value { get; }
 
				public EnumerableItem(int value) => Value = value;
 
				public IEnumerator<string> GetEnumerator() => Enumerable.Repeat("", Value).GetEnumerator();
 
				IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
			}
		}
 
		public class ArraysWithFunc
		{
			// https://github.com/xunit/xunit/issues/2795
			[Fact]
			public void CollectionItemIsEnumerable()
			{
				var actual = new EnumerableItem[] { new(0), new(2) };
				var expected = new EnumerableItem[] { new(1), new(3) };
 
				Assert.Equal(expected, actual, (x, y) => x.Value / 2 == y.Value / 2);
			}
 
			public sealed class EnumerableItem : IEnumerable<string>
			{
				public int Value { get; }
 
				public EnumerableItem(int value) => Value = value;
 
				public IEnumerator<string> GetEnumerator() => Enumerable.Repeat("", Value).GetEnumerator();
 
				IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
			}
		}
 
		public class Collections
		{
			[Fact]
			public static void Equal()
			{
				var expected = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
				var actual = new List<int>(expected);
 
				Assert.Equal(expected, actual);
			}
 
			[Theory]
			// Nulls
			[InlineData(
				null, null, "          null",
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "List<int> [1, 2, 3, 4, 5, $$ELLIPSIS$$]", null
			)]
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, null, "int[] [1, 2, 3, 4, 5, $$ELLIPSIS$$]",
				null, "      null", null
			)]
			// Start of array
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "           ↓ (pos 0)", "int[]     [1, 2, 3, 4, 5, $$ELLIPSIS$$]",
				new[] { 99, 2, 3, 4, 5, 6, 7 }, "List<int> [99, 2, 3, 4, 5, $$ELLIPSIS$$]", "           ↑ (pos 0)"
			)]
			// Middle of array
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "                      ↓ (pos 3)", "int[]     [$$ELLIPSIS$$, 2, 3, 4, 5, 6, $$ELLIPSIS$$]",
				new[] { 1, 2, 3, 99, 5, 6, 7 }, "List<int> [$$ELLIPSIS$$, 2, 3, 99, 5, 6, $$ELLIPSIS$$]", "                      ↑ (pos 3)"
			)]
			// End of array
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "                            ↓ (pos 6)", "int[]     [$$ELLIPSIS$$, 3, 4, 5, 6, 7]",
				new[] { 1, 2, 3, 4, 5, 6, 99 }, "List<int> [$$ELLIPSIS$$, 3, 4, 5, 6, 99]", "                            ↑ (pos 6)"
			)]
			// Overruns
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, null, "int[]     [$$ELLIPSIS$$, 4, 5, 6, 7]",
				new[] { 1, 2, 3, 4, 5, 6, 7, 8 }, "List<int> [$$ELLIPSIS$$, 4, 5, 6, 7, 8]", "                            ↑ (pos 7)"
			)]
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7 }, null, "int[]     [$$ELLIPSIS$$, 6, 7]",
				new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, "List<int> [$$ELLIPSIS$$, 6, 7, 8, 9, 10, $$ELLIPSIS$$]", "                      ↑ (pos 7)"
			)]
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7, 8 }, "                            ↓ (pos 7)", "int[]     [$$ELLIPSIS$$, 4, 5, 6, 7, 8]",
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "List<int> [$$ELLIPSIS$$, 4, 5, 6, 7]", null
			)]
			[InlineData(
				new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, "                      ↓ (pos 7)", "int[]     [$$ELLIPSIS$$, 6, 7, 8, 9, 10, $$ELLIPSIS$$]",
				new[] { 1, 2, 3, 4, 5, 6, 7 }, "List<int> [$$ELLIPSIS$$, 6, 7]", null
			)]
			public void NotEqual(
				int[]? expected,
				string? expectedPointer,
				string expectedDisplay,
				int[]? actualArray,
				string actualDisplay,
				string? actualPointer)
			{
				var actual = actualArray is null ? null : new List<int>(actualArray);
				var message = "Assert.Equal() Failure: Collections differ";
 
				if (expectedPointer is not null)
					message += Environment.NewLine + "          " + expectedPointer;
 
				message +=
					Environment.NewLine + "Expected: " + expectedDisplay +
					Environment.NewLine + "Actual:   " + actualDisplay;
 
				if (actualPointer is not null)
					message += Environment.NewLine + "          " + actualPointer;
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(message.Replace("$$ELLIPSIS$$", ArgumentFormatter.Ellipsis), ex.Message);
			}
		}
 
		public class CollectionsWithComparer
		{
			[Fact]
			public static void AlwaysFalse()
			{
				var expected = new[] { 1, 2, 3, 4, 5 };
				var actual = new List<int>(new int[] { 1, 2, 3, 4, 5 });
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual, new IntComparer(false)));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Collections differ" + Environment.NewLine +
					"                     ↓ (pos 0)" + Environment.NewLine +
					"Expected: int[]     [1, 2, 3, 4, 5]" + Environment.NewLine +
					"Actual:   List<int> [1, 2, 3, 4, 5]" + Environment.NewLine +
					"                     ↑ (pos 0)",
					ex.Message
				);
			}
 
			[Fact]
			public static void AlwaysTrue()
			{
				var expected = new[] { 1, 2, 3, 4, 5 };
				var actual = new List<int>(new int[] { 0, 0, 0, 0, 0 });
 
				Assert.Equal(expected, actual, new IntComparer(true));
			}
 
			class IntComparer : IEqualityComparer<int>
			{
				readonly bool answer;
 
				public IntComparer(bool answer)
				{
					this.answer = answer;
				}
 
				public bool Equals(int x, int y) => answer;
 
				public int GetHashCode(int obj) => throw new NotImplementedException();
			}
 
			// https://github.com/xunit/xunit/issues/2795
			[Fact]
			public void CollectionItemIsEnumerable()
			{
				List<EnumerableItem> actual = new List<EnumerableItem> { new(0), new(2) };
				List<EnumerableItem> expected = new List<EnumerableItem> { new(1), new(3) };
 
				Assert.Equal(expected, actual, new EnumerableItemComparer());
			}
 
			public class EnumerableItemComparer : IEqualityComparer<EnumerableItem>
			{
				public bool Equals(EnumerableItem? x, EnumerableItem? y) =>
					x?.Value / 2 == y?.Value / 2;
 
				public int GetHashCode(EnumerableItem obj) =>
					throw new NotImplementedException();
			}
 
			public sealed class EnumerableItem : IEnumerable<string>
			{
				public int Value { get; }
 
				public EnumerableItem(int value) => Value = value;
 
				public IEnumerator<string> GetEnumerator() => Enumerable.Repeat("", Value).GetEnumerator();
 
				IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
			}
 
			[Fact]
			public void WithThrow_PrintsPointerWhereThrowOccurs_RecordsInnerException()
			{
				var ex = Record.Exception(() => Assert.Equal(new[] { 1, 2 }, new[] { 1, 3 }, new ThrowingComparer()));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Exception thrown during comparison" + Environment.NewLine +
					"           ↓ (pos 0)" + Environment.NewLine +
					"Expected: [1, 2]" + Environment.NewLine +
					"Actual:   [1, 3]" + Environment.NewLine +
					"           ↑ (pos 0)",
					ex.Message
				);
				Assert.IsType<DivideByZeroException>(ex.InnerException);
			}
 
			public class ThrowingComparer : IEqualityComparer<int>
			{
				public bool Equals(int x, int y) =>
					throw new DivideByZeroException();
 
				public int GetHashCode(int obj) =>
					throw new NotImplementedException();
			}
		}
 
		public class CollectionsWithEquatable
		{
#if XUNIT_AOT
			[Fact(Skip = "Not supported with AOT")]
#else
			[Fact]
#endif
			public void Equal()
			{
				var expected = new[] { new EquatableObject { Char = 'a' } };
				var actual = new[] { new EquatableObject { Char = 'a' } };
 
				Assert.Equal(expected, actual);
			}
 
			[Fact]
			public void NotEqual()
			{
				var expected = new[] { new EquatableObject { Char = 'a' } };
				var actual = new[] { new EquatableObject { Char = 'b' } };
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Collections differ" + Environment.NewLine +
					"           ↓ (pos 0)" + Environment.NewLine +
					"Expected: [EquatableObject { Char = 'a' }]" + Environment.NewLine +
					"Actual:   [EquatableObject { Char = 'b' }]" + Environment.NewLine +
					"           ↑ (pos 0)",
					ex.Message
				);
			}
 
			public class EquatableObject : IEquatable<EquatableObject>
			{
				public char Char { get; set; }
 
				public bool Equals(EquatableObject? other) =>
					other != null && other.Char == Char;
			}
		}
 
		public class CollectionsWithFunc
		{
			[Fact]
			public static void AlwaysFalse()
			{
				var expected = new[] { 1, 2, 3, 4, 5 };
				var actual = new List<int>(new int[] { 1, 2, 3, 4, 5 });
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual, (x, y) => false));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Collections differ" + Environment.NewLine +
					"                     ↓ (pos 0)" + Environment.NewLine +
					"Expected: int[]     [1, 2, 3, 4, 5]" + Environment.NewLine +
					"Actual:   List<int> [1, 2, 3, 4, 5]" + Environment.NewLine +
					"                     ↑ (pos 0)",
					ex.Message
				);
			}
 
			[Fact]
			public static void AlwaysTrue()
			{
				var expected = new[] { 1, 2, 3, 4, 5 };
				var actual = new List<int>(new int[] { 0, 0, 0, 0, 0 });
 
				Assert.Equal(expected, actual, (x, y) => true);
			}
 
			// https://github.com/xunit/xunit/issues/2795
			[Fact]
			public void CollectionItemIsEnumerable()
			{
				List<EnumerableItem> actual = new List<EnumerableItem> { new(0), new(2) };
				List<EnumerableItem> expected = new List<EnumerableItem> { new(1), new(3) };
 
				Assert.Equal(expected, actual, (x, y) => x.Value / 2 == y.Value / 2);
			}
 
			public sealed class EnumerableItem : IEnumerable<string>
			{
				public int Value { get; }
 
				public EnumerableItem(int value) => Value = value;
 
				public IEnumerator<string> GetEnumerator() => Enumerable.Repeat("", Value).GetEnumerator();
 
				IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
			}
 
			[Fact]
			public void WithThrow_PrintsPointerWhereThrowOccurs_RecordsInnerException()
			{
				var ex = Record.Exception(() =>
					Assert.Equal(
						new[] { 1, 2 },
						new[] { 1, 3 },
						(int e, int a) => throw new DivideByZeroException()
					)
				);
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Exception thrown during comparison" + Environment.NewLine +
					"           ↓ (pos 0)" + Environment.NewLine +
					"Expected: [1, 2]" + Environment.NewLine +
					"Actual:   [1, 3]" + Environment.NewLine +
					"           ↑ (pos 0)",
					ex.Message
				);
				Assert.IsType<DivideByZeroException>(ex.InnerException);
			}
		}
 
		public class Dictionaries
		{
			[Fact]
			public static void InOrderDictionary()
			{
				var expected = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
				var actual = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
 
				Assert.Equal(expected, actual);
			}
 
			[Fact]
			public static void OutOfOrderDictionary()
			{
				var expected = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
				var actual = new Dictionary<string, int> { { "b", 2 }, { "c", 3 }, { "a", 1 } };
 
				Assert.Equal(expected, actual);
			}
 
			[Fact]
			public static void ExpectedLarger()
			{
				var expected = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
				var actual = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Dictionaries differ" + Environment.NewLine +
					"Expected: [[\"a\"] = 1, [\"b\"] = 2, [\"c\"] = 3]" + Environment.NewLine +
					"Actual:   [[\"a\"] = 1, [\"b\"] = 2]",
					ex.Message
				);
			}
 
			[Fact]
			public static void ActualLarger()
			{
				var expected = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
				var actual = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Dictionaries differ" + Environment.NewLine +
					"Expected: [[\"a\"] = 1, [\"b\"] = 2]" + Environment.NewLine +
					"Actual:   [[\"a\"] = 1, [\"b\"] = 2, [\"c\"] = 3]",
					ex.Message
				);
			}
 
			[Fact]
			public static void SomeKeysDiffer()
			{
				var expected = new Dictionary<string, int>
				{
					["a"] = 1,
					["be"] = 2,
					["c"] = 3,
					["d"] = 4,
					["e"] = 5,
					["f"] = 6,
				};
				var actual = new Dictionary<string, int>
				{
					["a"] = 1,
					["ba"] = 2,
					["c"] = 3,
					["d"] = 4,
					["e"] = 5,
					["f"] = 6,
				};
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Dictionaries differ" + Environment.NewLine +
					$"Expected: [[\"a\"] = 1, [\"be\"] = 2, [\"c\"] = 3, [\"d\"] = 4, [\"e\"] = 5, {ArgumentFormatter.Ellipsis}]" + Environment.NewLine +
					$"Actual:   [[\"a\"] = 1, [\"ba\"] = 2, [\"c\"] = 3, [\"d\"] = 4, [\"e\"] = 5, {ArgumentFormatter.Ellipsis}]",
					ex.Message
				);
			}
 
			[Fact]
			public static void WithCollectionValues_Equal()
			{
				// Different concrete collection types in the value slot, per https://github.com/xunit/xunit/issues/2850
				var expected = new Dictionary<string, IEnumerable<string>>
				{
					["toAddresses"] = new List<string> { "test1@example.com" },
					["ccAddresses"] = new List<string> { "test2@example.com" },
				};
				var actual = new Dictionary<string, IEnumerable<string>>
				{
					["toAddresses"] = new string[] { "test1@example.com" },
					["ccAddresses"] = new string[] { "test2@example.com" },
				};
 
				Assert.Equal(expected, actual);
			}
 
			[Fact]
			public static void WithCollectionValues_NotEqual()
			{
				// Different concrete collection types in the value slot, per https://github.com/xunit/xunit/issues/2850
				var expected = new Dictionary<string, IEnumerable<string>>
				{
					["toAddresses"] = new List<string> { "test1@example.com" },
					["ccAddresses"] = new List<string> { "test2@example.com" },
				};
				var actual = new Dictionary<string, IEnumerable<string>>
				{
					["toAddresses"] = new string[] { "test1@example.com" },
					["ccAddresses"] = new string[] { "test3@example.com" },
				};
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Dictionaries differ" + Environment.NewLine +
					"Expected: [[\"toAddresses\"] = [\"test1@example.com\"], [\"ccAddresses\"] = [\"test2@example.com\"]]" + Environment.NewLine +
					"Actual:   [[\"toAddresses\"] = [\"test1@example.com\"], [\"ccAddresses\"] = [\"test3@example.com\"]]",
					ex.Message
				);
			}
 
#if XUNIT_AOT
			[Fact(Skip = "Not supported with AOT")]
#else
			[Fact]
#endif
			public void EquatableValues_Equal()
			{
				var expected = new Dictionary<string, EquatableObject> { { "Key1", new() { Char = 'a' } } };
				var actual = new Dictionary<string, EquatableObject> { { "Key1", new() { Char = 'a' } } };
 
				Assert.Equal(expected, actual);
			}
 
			[Fact]
			public void EquatableValues_NotEqual()
			{
				var expected = new Dictionary<string, EquatableObject> { { "Key1", new() { Char = 'a' } } };
				var actual = new Dictionary<string, EquatableObject> { { "Key1", new() { Char = 'b' } } };
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Dictionaries differ" + Environment.NewLine +
					"Expected: [[\"Key1\"] = EquatableObject { Char = 'a' }]" + Environment.NewLine +
					"Actual:   [[\"Key1\"] = EquatableObject { Char = 'b' }]",
					ex.Message
				);
			}
 
			public class EquatableObject : IEquatable<EquatableObject>
			{
				public char Char { get; set; }
 
				public bool Equals(EquatableObject? other) =>
					other != null && other.Char == Char;
			}
 
			[Fact]
			public void ComplexEmbeddedValues_Equal()
			{
				var expected = new Dictionary<string, object>()
				{
					["key"] = new Dictionary<string, object>()
					{
						["key"] = new List<Dictionary<string, object>>()
						{
							new Dictionary<string, object>()
							{
								["key"] = new List<object> { "value" }
							}
						}
					}
				};
				var actual = new Dictionary<string, object>()
				{
					["key"] = new Dictionary<string, object>()
					{
						["key"] = new List<Dictionary<string, object>>()
						{
							new Dictionary<string, object>()
							{
								["key"] = new List<object> { "value" }
							}
						}
					}
				};
 
				Assert.Equal(expected, actual);
			}
 
			[Fact]
			public void ComplexEmbeddedValues_NotEqual()
			{
				var expected = new Dictionary<string, object>()
				{
					["key"] = new Dictionary<string, object>()
					{
						["key"] = new List<Dictionary<string, object>>()
						{
							new Dictionary<string, object>()
							{
								["key"] = new List<object> { "value1" }
							}
						}
					}
				};
				var actual = new Dictionary<string, object>()
				{
					["key"] = new Dictionary<string, object>()
					{
						["key"] = new List<Dictionary<string, object>>()
						{
							new Dictionary<string, object>()
							{
								["key"] = new List<object> { "value2" }
							}
						}
					}
				};
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: Dictionaries differ" + Environment.NewLine +
					"Expected: [[\"key\"] = [[\"key\"] = [[[\"key\"] = [\"value1\"]]]]]" + Environment.NewLine +
					"Actual:   [[\"key\"] = [[\"key\"] = [[[\"key\"] = [\"value2\"]]]]]",
					ex.Message
				);
			}
		}
 
		public class Sets
		{
			[Fact]
			public void Equal()
			{
				var expected = new HashSet<int> { 42, 2112 };
				var actual = new HashSet<int> { 2112, 42 };
 
				Assert.Equal(expected, actual);
			}
 
#if XUNIT_AOT
			[Fact(Skip = "Not supported with AOT")]
#else
			[Fact]
#endif
			public void Equal_WithInternalComparer()
			{
				var comparer = new BitArrayComparer();
				var expected = new HashSet<BitArray>(comparer) { new BitArray(new[] { true, false }) };
				var actual = new HashSet<BitArray>(comparer) { new BitArray(new[] { true, false }) };
 
				Assert.Equal(expected, actual);
			}
 
#if XUNIT_AOT
			[Fact(Skip = "Not supported with AOT")]
#else
			[Fact]
#endif
			public void Equal_WithExternalComparer()
			{
				var expected = new HashSet<BitArray> { new BitArray(new[] { true, false }) };
				var actual = new HashSet<BitArray> { new BitArray(new[] { true, false }) };
 
				Assert.Equal(expected, actual, new BitArrayComparer());
			}
 
			[Fact]
			public void NotEqual()
			{
				var expected = new HashSet<int> { 42, 2112 };
				var actual = new HashSet<int> { 2600, 42 };
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: HashSets differ" + Environment.NewLine +
					"Expected: [42, 2112]" + Environment.NewLine +
					"Actual:   [2600, 42]",
					ex.Message
				);
			}
 
			[Fact]
			public void NotEqual_WithInternalComparer()
			{
				var comparer = new BitArrayComparer();
				var expected = new HashSet<BitArray>(comparer) { new BitArray(new[] { true, false }) };
				var actual = new HashSet<BitArray>(comparer) { new BitArray(new[] { true, true }) };
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: HashSets differ" + Environment.NewLine +
					"Expected: [[True, False]]" + Environment.NewLine +
					"Actual:   [[True, True]]",
					ex.Message
				);
			}
 
			[Fact]
			public void NotEqual_WithExternalComparer()
			{
				var expected = new HashSet<BitArray> { new BitArray(new[] { true, false }) };
				var actual = new HashSet<BitArray> { new BitArray(new[] { true, true }) };
 
				var ex = Record.Exception(() => Assert.Equal(expected, actual, new BitArrayComparer()));
 
				Assert.IsType<EqualException>(ex);
				Assert.Equal(
					"Assert.Equal() Failure: HashSets differ" + Environment.NewLine +
					"Expected: [[True, False]]" + Environment.NewLine +
					"Actual:   [[True, True]]",
					ex.Message
				);
			}
 
			public class BitArrayComparer : IEqualityComparer<BitArray>
			{
				public bool Equals(
					BitArray? x,
					BitArray? y) =>
						ToBitString(x) == ToBitString(y);
 
				public int GetHashCode(BitArray obj) =>
					ToBitString(obj).GetHashCode();
 
				static string ToBitString(BitArray? bitArray)
				{
					if (bitArray is null)
						return string.Empty;
 
					var sb = new StringBuilder(bitArray.Length);
 
					for (int idx = 0; idx < bitArray.Length; ++idx)
						sb.Append(bitArray[idx] ? '1' : '0');
 
					return sb.ToString();
				}
			}
		}
	}
 
	public class NotEmpty
	{
		[Fact]
		public static void EmptyContainer()
		{
			var list = new List<int>();
 
			var ex = Record.Exception(() => Assert.NotEmpty(list));
 
			Assert.IsType<NotEmptyException>(ex);
			Assert.Equal(
				"Assert.NotEmpty() Failure: Collection was empty",
				ex.Message
			);
		}
 
		[Fact]
		public static void NonEmptyContainer()
		{
			var list = new List<int> { 42 };
 
			Assert.NotEmpty(list);
		}
 
		[Fact]
		public static void EnumeratorDisposed()
		{
			var enumerator = new SpyEnumerator<int>(Enumerable.Range(0, 1));
 
			Assert.NotEmpty(enumerator);
 
			Assert.True(enumerator.IsDisposed);
		}
	}
 
	public class NotEqual
	{
		public class Null
		{
			[Fact]
			public static void BothNull()
			{
				var expected = default(IEnumerable<int>);
				var actual = default(IEnumerable<int>);
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Values are equal" + Environment.NewLine +
					"Expected: Not null" + Environment.NewLine +
					"Actual:       null",
					ex.Message
				);
			}
 
			[Fact]
			public static void EmptyExpectedNullActual()
			{
				var expected = new int[0];
				var actual = default(IEnumerable<int>);
 
				Assert.NotEqual(expected, actual);
			}
 
			[Fact]
			public static void NullExpectedEmptyActual()
			{
				var expected = default(IEnumerable<int>);
				var actual = new int[0];
 
				Assert.NotEqual(expected, actual);
			}
		}
 
		public class Arrays
		{
			[Fact]
			public static void Equal()
			{
				string[] expected = { "@", "a", "ab", "b" };
				string[] actual = { "@", "a", "ab", "b" };
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Collections are equal" + Environment.NewLine +
					"Expected: Not [\"@\", \"a\", \"ab\", \"b\"]" + Environment.NewLine +
					"Actual:       [\"@\", \"a\", \"ab\", \"b\"]",
					ex.Message
				);
			}
 
			[Fact]
			public static void EmbeddedArrays_Equal()
			{
				string[][] expected = { new[] { "@", "a" }, new[] { "ab", "b" } };
				string[][] actual = { new[] { "@", "a" }, new[] { "ab", "b" } };
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Collections are equal" + Environment.NewLine +
					"Expected: Not [[\"@\", \"a\"], [\"ab\", \"b\"]]" + Environment.NewLine +
					"Actual:       [[\"@\", \"a\"], [\"ab\", \"b\"]]",
					ex.Message
				);
			}
 
			[Fact]
			public static void NotEqual()
			{
				IEnumerable<int> expected = new[] { 1, 2, 3 };
				IEnumerable<int> actual = new[] { 1, 2, 4 };
 
				Assert.NotEqual(expected, actual);
			}
 
			[Fact]
			public static void SameValueDifferentType()
			{
				Assert.NotEqual(new object[] { 1, 2, 3 }, new object[] { 1, 2, 3L });
			}
		}
 
		public class Collections
		{
			[Fact]
			public static void Equal()
			{
				var expected = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
				var actual = new List<int>(expected);
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Collections are equal" + Environment.NewLine +
					$"Expected: Not int[]     [1, 2, 3, 4, 5, {ArgumentFormatter.Ellipsis}]" + Environment.NewLine +
					$"Actual:       List<int> [1, 2, 3, 4, 5, {ArgumentFormatter.Ellipsis}]",
					ex.Message
				);
			}
 
			[Fact]
			public static void NotEqual()
			{
				var expected = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
				var actual = new List<int>(new[] { 1, 2, 3, 4, 0, 6, 7, 8, 9, 10 });
 
				Assert.NotEqual(expected, actual);
			}
		}
 
		public class CollectionsWithComparer
		{
			[Fact]
			public static void AlwaysFalse()
			{
				var expected = new[] { 1, 2, 3, 4, 5 };
				var actual = new List<int>(new int[] { 1, 2, 3, 4, 5 });
 
				Assert.NotEqual(expected, actual, new IntComparer(false));
			}
 
			[Fact]
			public static void AlwaysTrue()
			{
				var expected = new[] { 1, 2, 3, 4, 5 };
				var actual = new List<int>(new int[] { 0, 0, 0, 0, 0 });
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual, new IntComparer(true)));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Collections are equal" + Environment.NewLine +
					"Expected: Not int[]     [1, 2, 3, 4, 5]" + Environment.NewLine +
					"Actual:       List<int> [0, 0, 0, 0, 0]",
					ex.Message
				);
			}
 
			class IntComparer : IEqualityComparer<int>
			{
				readonly bool answer;
 
				public IntComparer(bool answer)
				{
					this.answer = answer;
				}
 
				public bool Equals(int x, int y) => answer;
 
				public int GetHashCode(int obj) => throw new NotImplementedException();
			}
 
			[Fact]
			public void WithThrow_PrintsPointerWhereThrowOccurs_RecordsInnerException()
			{
				var ex = Record.Exception(() => Assert.NotEqual(new[] { 1, 2 }, new[] { 1, 2 }, new ThrowingComparer()));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Exception thrown during comparison" + Environment.NewLine +
					"               ↓ (pos 0)" + Environment.NewLine +
					"Expected: Not [1, 2]" + Environment.NewLine +
					"Actual:       [1, 2]" + Environment.NewLine +
					"               ↑ (pos 0)",
					ex.Message
				);
				Assert.IsType<DivideByZeroException>(ex.InnerException);
			}
 
			public class ThrowingComparer : IEqualityComparer<int>
			{
				public bool Equals(int x, int y) =>
					throw new DivideByZeroException();
 
				public int GetHashCode(int obj) =>
					throw new NotImplementedException();
			}
		}
 
		public class CollectionsWithEquatable
		{
#if XUNIT_AOT
			[Fact(Skip = "Not supported with AOT")]
#else
			[Fact]
#endif
			public void Equal()
			{
				var expected = new[] { new EquatableObject { Char = 'a' } };
				var actual = new[] { new EquatableObject { Char = 'a' } };
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Collections are equal" + Environment.NewLine +
					"Expected: Not [EquatableObject { Char = 'a' }]" + Environment.NewLine +
					"Actual:       [EquatableObject { Char = 'a' }]",
					ex.Message
				);
			}
 
			[Fact]
			public void NotEqual()
			{
				var expected = new[] { new EquatableObject { Char = 'a' } };
				var actual = new[] { new EquatableObject { Char = 'b' } };
 
				Assert.NotEqual(expected, actual);
			}
 
			public class EquatableObject : IEquatable<EquatableObject>
			{
				public char Char { get; set; }
 
				public bool Equals(EquatableObject? other) =>
					other != null && other.Char == Char;
			}
		}
 
		public class CollectionsWithFunc
		{
			[Fact]
			public static void AlwaysFalse()
			{
				var expected = new[] { 1, 2, 3, 4, 5 };
				var actual = new List<int>(new int[] { 1, 2, 3, 4, 5 });
 
				Assert.NotEqual(expected, actual, (x, y) => false);
			}
 
			[Fact]
			public static void AlwaysTrue()
			{
				var expected = new[] { 1, 2, 3, 4, 5 };
				var actual = new List<int>(new int[] { 0, 0, 0, 0, 0 });
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual, (x, y) => true));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Collections are equal" + Environment.NewLine +
					"Expected: Not int[]     [1, 2, 3, 4, 5]" + Environment.NewLine +
					"Actual:       List<int> [0, 0, 0, 0, 0]",
					ex.Message
				);
			}
 
			[Fact]
			public void WithThrow_PrintsPointerWhereThrowOccurs_RecordsInnerException()
			{
				var ex = Record.Exception(() =>
					Assert.NotEqual(
						new[] { 1, 2 },
						new[] { 1, 2 },
						(int e, int a) => throw new DivideByZeroException()
					)
				);
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Exception thrown during comparison" + Environment.NewLine +
					"               ↓ (pos 0)" + Environment.NewLine +
					"Expected: Not [1, 2]" + Environment.NewLine +
					"Actual:       [1, 2]" + Environment.NewLine +
					"               ↑ (pos 0)",
					ex.Message
				);
				Assert.IsType<DivideByZeroException>(ex.InnerException);
			}
		}
 
		public class Dictionaries
		{
			[Fact]
			public static void InOrderDictionary()
			{
				var expected = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
				var actual = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Dictionaries are equal" + Environment.NewLine +
					"Expected: Not [[\"a\"] = 1, [\"b\"] = 2, [\"c\"] = 3]" + Environment.NewLine +
					"Actual:       [[\"a\"] = 1, [\"b\"] = 2, [\"c\"] = 3]",
					ex.Message
				);
			}
 
			[Fact]
			public static void OutOfOrderDictionary()
			{
				var expected = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
				var actual = new Dictionary<string, int> { { "b", 2 }, { "c", 3 }, { "a", 1 } };
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Dictionaries are equal" + Environment.NewLine +
					"Expected: Not [[\"a\"] = 1, [\"b\"] = 2, [\"c\"] = 3]" + Environment.NewLine +
					"Actual:       [[\"b\"] = 2, [\"c\"] = 3, [\"a\"] = 1]",
					ex.Message
				);
			}
 
			[Fact]
			public static void ExpectedLarger()
			{
				var expected = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
				var actual = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
 
				Assert.NotEqual(expected, actual);
			}
 
			[Fact]
			public static void ActualLarger()
			{
				var expected = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
				var actual = new Dictionary<string, int> { { "a", 1 }, { "b", 2 }, { "c", 3 } };
 
				Assert.NotEqual(expected, actual);
			}
 
			[Fact]
			public static void SomeKeysDiffer()
			{
				var expected = new Dictionary<string, int>
				{
					["a"] = 1,
					["be"] = 2,
					["c"] = 3,
					["d"] = 4,
					["e"] = 5,
					["f"] = 6,
				};
				var actual = new Dictionary<string, int>
				{
					["a"] = 1,
					["ba"] = 2,
					["c"] = 3,
					["d"] = 4,
					["e"] = 5,
					["f"] = 6,
				};
 
				Assert.NotEqual(expected, actual);
			}
 
			[Fact]
			public static void WithCollectionValues_Equal()
			{
				// Different concrete collection types in the value slot, per https://github.com/xunit/xunit/issues/2850
				var expected = new Dictionary<string, IEnumerable<string>>
				{
					["toAddresses"] = new List<string> { "test1@example.com" },
					["ccAddresses"] = new List<string> { "test2@example.com" },
				};
				var actual = new Dictionary<string, IEnumerable<string>>
				{
					["toAddresses"] = new string[] { "test1@example.com" },
					["ccAddresses"] = new string[] { "test2@example.com" },
				};
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Dictionaries are equal" + Environment.NewLine +
					"Expected: Not [[\"toAddresses\"] = [\"test1@example.com\"], [\"ccAddresses\"] = [\"test2@example.com\"]]" + Environment.NewLine +
					"Actual:       [[\"toAddresses\"] = [\"test1@example.com\"], [\"ccAddresses\"] = [\"test2@example.com\"]]",
					ex.Message
				);
			}
 
			[Fact]
			public static void WithCollectionValues_NotEqual()
			{
				// Different concrete collection types in the value slot, per https://github.com/xunit/xunit/issues/2850
				var expected = new Dictionary<string, IEnumerable<string>>
				{
					["toAddresses"] = new List<string> { "test1@example.com" },
					["ccAddresses"] = new List<string> { "test2@example.com" },
				};
				var actual = new Dictionary<string, IEnumerable<string>>
				{
					["toAddresses"] = new string[] { "test1@example.com" },
					["ccAddresses"] = new string[] { "test3@example.com" },
				};
 
				Assert.NotEqual(expected, actual);
			}
 
#if XUNIT_AOT
			[Fact(Skip = "Not supported with AOT")]
#else
			[Fact]
#endif
			public void EquatableValues_Equal()
			{
				var expected = new Dictionary<string, EquatableObject> { { "Key1", new() { Char = 'a' } } };
				var actual = new Dictionary<string, EquatableObject> { { "Key1", new() { Char = 'a' } } };
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Dictionaries are equal" + Environment.NewLine +
					"Expected: Not [[\"Key1\"] = EquatableObject { Char = 'a' }]" + Environment.NewLine +
					"Actual:       [[\"Key1\"] = EquatableObject { Char = 'a' }]",
					ex.Message
				);
			}
 
			[Fact]
			public void EquatableValues_NotEqual()
			{
				var expected = new Dictionary<string, EquatableObject> { { "Key1", new() { Char = 'a' } } };
				var actual = new Dictionary<string, EquatableObject> { { "Key1", new() { Char = 'b' } } };
 
				Assert.NotEqual(expected, actual);
			}
 
			public class EquatableObject : IEquatable<EquatableObject>
			{
				public char Char { get; set; }
 
				public bool Equals(EquatableObject? other) =>
					other != null && other.Char == Char;
			}
 
			[Fact]
			public void ComplexEmbeddedValues_Equal()
			{
				var expected = new Dictionary<string, object>()
				{
					["key"] = new Dictionary<string, object>()
					{
						["key"] = new List<Dictionary<string, object>>()
						{
							new Dictionary<string, object>()
							{
								["key"] = new List<object> { "value" }
							}
						}
					}
				};
				var actual = new Dictionary<string, object>()
				{
					["key"] = new Dictionary<string, object>()
					{
						["key"] = new List<Dictionary<string, object>>()
						{
							new Dictionary<string, object>()
							{
								["key"] = new List<object> { "value" }
							}
						}
					}
				};
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: Dictionaries are equal" + Environment.NewLine +
					"Expected: Not [[\"key\"] = [[\"key\"] = [[[\"key\"] = [\"value\"]]]]]" + Environment.NewLine +
					"Actual:       [[\"key\"] = [[\"key\"] = [[[\"key\"] = [\"value\"]]]]]",
					ex.Message
				);
			}
 
			[Fact]
			public void ComplexEmbeddedValues_NotEqual()
			{
				var expected = new Dictionary<string, object>()
				{
					["key"] = new Dictionary<string, object>()
					{
						["key"] = new List<Dictionary<string, object>>()
						{
							new Dictionary<string, object>()
							{
								["key"] = new List<object> { "value1" }
							}
						}
					}
				};
				var actual = new Dictionary<string, object>()
				{
					["key"] = new Dictionary<string, object>()
					{
						["key"] = new List<Dictionary<string, object>>()
						{
							new Dictionary<string, object>()
							{
								["key"] = new List<object> { "value2" }
							}
						}
					}
				};
 
				Assert.NotEqual(expected, actual);
			}
		}
 
		public class Sets
		{
			[Fact]
			public void Equal()
			{
				var expected = new HashSet<int> { 42, 2112 };
				var actual = new HashSet<int> { 2112, 42 };
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: HashSets are equal" + Environment.NewLine +
					"Expected: Not [42, 2112]" + Environment.NewLine +
					"Actual:       [2112, 42]",
					ex.Message
				);
			}
 
#if XUNIT_AOT
			[Fact(Skip = "Not supported with AOT")]
#else
			[Fact]
#endif
			public void Equal_WithInternalComparer()
			{
				var comparer = new BitArrayComparer();
				var expected = new HashSet<BitArray>(comparer) { new BitArray(new[] { true, false }) };
				var actual = new HashSet<BitArray>(comparer) { new BitArray(new[] { true, false }) };
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: HashSets are equal" + Environment.NewLine +
					"Expected: Not [[True, False]]" + Environment.NewLine +
					"Actual:       [[True, False]]",
					ex.Message
				);
			}
 
#if XUNIT_AOT
			[Fact(Skip = "Not supported with AOT")]
#else
			[Fact]
#endif
			public void Equal_WithExternalComparer()
			{
				var expected = new HashSet<BitArray> { new BitArray(new[] { true, false }) };
				var actual = new HashSet<BitArray> { new BitArray(new[] { true, false }) };
 
				var ex = Record.Exception(() => Assert.NotEqual(expected, actual, new BitArrayComparer()));
 
				Assert.IsType<NotEqualException>(ex);
				Assert.Equal(
					"Assert.NotEqual() Failure: HashSets are equal" + Environment.NewLine +
					"Expected: Not [[True, False]]" + Environment.NewLine +
					"Actual:       [[True, False]]",
					ex.Message
				);
			}
 
			[Fact]
			public void NotEqual()
			{
				var expected = new HashSet<int> { 42, 2112 };
				var actual = new HashSet<int> { 2600, 42 };
 
				Assert.NotEqual(expected, actual);
			}
 
			[Fact]
			public void NotEqual_WithInternalComparer()
			{
				var comparer = new BitArrayComparer();
				var expected = new HashSet<BitArray>(comparer) { new BitArray(new[] { true, false }) };
				var actual = new HashSet<BitArray>(comparer) { new BitArray(new[] { true, true }) };
 
				Assert.NotEqual(expected, actual);
			}
 
			[Fact]
			public void NotEqual_WithExternalComparer()
			{
				var expected = new HashSet<BitArray> { new BitArray(new[] { true, false }) };
				var actual = new HashSet<BitArray> { new BitArray(new[] { true, true }) };
 
				Assert.NotEqual(expected, actual, new BitArrayComparer());
			}
 
			public class BitArrayComparer : IEqualityComparer<BitArray>
			{
				public bool Equals(
					BitArray? x,
					BitArray? y) =>
						ToBitString(x) == ToBitString(y);
 
				public int GetHashCode(BitArray obj) =>
					ToBitString(obj).GetHashCode();
 
				static string ToBitString(BitArray? bitArray)
				{
					if (bitArray is null)
						return string.Empty;
 
					var sb = new StringBuilder(bitArray.Length);
 
					for (int idx = 0; idx < bitArray.Length; ++idx)
						sb.Append(bitArray[idx] ? '1' : '0');
 
					return sb.ToString();
				}
			}
		}
	}
 
	public class Single_NonGeneric
	{
		[Fact]
		public static void GuardClause()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.Single(null!));
		}
 
		[Fact]
		public static void EmptyCollection()
		{
			var collection = new ArrayList();
 
			var ex = Record.Exception(() => Assert.Single(collection));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal("Assert.Single() Failure: The collection was empty", ex.Message);
		}
 
		[Fact]
		public static void SingleItemCollection()
		{
			var collection = new ArrayList { "Hello" };
 
			var item = Assert.Single(collection);
 
			Assert.Equal("Hello", item);
		}
 
		[Fact]
		public static void MultiItemCollection()
		{
			var collection = new ArrayList { "Hello", "World" };
 
			var ex = Record.Exception(() => Assert.Single(collection));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection contained 2 items" + Environment.NewLine +
				"Collection: [\"Hello\", \"World\"]",
				ex.Message
			);
		}
 
		[Fact]
		public static void Truncation()
		{
			var collection = new ArrayList { 1, 2, 3, 4, 5, 6, 7 };
 
			var ex = Record.Exception(() => Assert.Single(collection));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection contained 7 items" + Environment.NewLine +
				$"Collection: [1, 2, 3, 4, 5, {ArgumentFormatter.Ellipsis}]",
				ex.Message
			);
		}
	}
 
	public class Single_NonGeneric_WithObject
	{
		[Fact]
		public static void GuardClause()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.Single(null!, null));
		}
 
		[Fact]
		public static void SingleMatch()
		{
			IEnumerable collection = new ArrayList { "Hello", "World" };
 
			Assert.Single(collection, "Hello");
		}
 
		[Fact]
		public static void SingleMatch_Null()
		{
			IEnumerable collection = new ArrayList { "Hello", "World!", null };
 
			Assert.Single(collection, null);
		}
 
		[Fact]
		public static void NoMatches()
		{
			IEnumerable collection = new ArrayList { "Hello", "World" };
 
			var ex = Record.Exception(() => Assert.Single(collection, "foo"));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection did not contain any matching items" + Environment.NewLine +
				"Expected:   \"foo\"" + Environment.NewLine +
				"Collection: [\"Hello\", \"World\"]",
				ex.Message
			);
		}
 
		[Fact]
		public static void TooManyMatches()
		{
			var collection = new ArrayList { "Hello", "World", "Hello" };
 
			var ex = Record.Exception(() => Assert.Single(collection, "Hello"));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection contained 2 matching items" + Environment.NewLine +
				"Expected:      \"Hello\"" + Environment.NewLine +
				"Collection:    [\"Hello\", \"World\", \"Hello\"]" + Environment.NewLine +
				"Match indices: 0, 2",
				ex.Message
			);
		}
 
		[Fact]
		public static void Truncation()
		{
			var collection = new ArrayList { 1, 2, 3, 4, 5, 6, 7, 8, 4 };
 
			var ex = Record.Exception(() => Assert.Single(collection, 4));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection contained 2 matching items" + Environment.NewLine +
				"Expected:      4" + Environment.NewLine +
				$"Collection:    [1, 2, 3, 4, 5, {ArgumentFormatter.Ellipsis}]" + Environment.NewLine +
				"Match indices: 3, 8",
				ex.Message
			);
		}
	}
 
	public class Single_Generic
	{
		[Fact]
		public static void GuardClause()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.Single(default(IEnumerable<object>)!));
		}
 
		[Fact]
		public static void EmptyCollection()
		{
			var collection = new object[0];
 
			var ex = Record.Exception(() => Assert.Single(collection));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal("Assert.Single() Failure: The collection was empty", ex.Message);
		}
 
		[Fact]
		public static void SingleItemCollection()
		{
			var collection = new[] { "Hello" };
 
			var item = Assert.Single(collection);
 
			Assert.Equal("Hello", item);
		}
 
		[Fact]
		public static void MultiItemCollection()
		{
			var collection = new[] { "Hello", "World" };
 
			var ex = Record.Exception(() => Assert.Single(collection));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection contained 2 items" + Environment.NewLine +
				"Collection: [\"Hello\", \"World\"]",
				ex.Message
			);
		}
 
		[Fact]
		public static void Truncation()
		{
			var collection = new[] { 1, 2, 3, 4, 5, 6, 7 };
 
			var ex = Record.Exception(() => Assert.Single(collection));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection contained 7 items" + Environment.NewLine +
				$"Collection: [1, 2, 3, 4, 5, {ArgumentFormatter.Ellipsis}]",
				ex.Message
			);
		}
 
		[Fact]
		public static void StringAsCollection_Match()
		{
			var collection = "H";
 
			var value = Assert.Single(collection);
 
			Assert.Equal('H', value);
		}
 
		[Fact]
		public static void StringAsCollection_NoMatch()
		{
			var collection = "Hello";
 
			var ex = Record.Exception(() => Assert.Single(collection));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection contained 5 items" + Environment.NewLine +
				"Collection: ['H', 'e', 'l', 'l', 'o']",
				ex.Message
			);
		}
	}
 
	public class Single_Generic_WithPredicate
	{
		[Fact]
		public static void GuardClauses()
		{
			Assert.Throws<ArgumentNullException>("collection", () => Assert.Single(default(IEnumerable<object>)!, _ => true));
			Assert.Throws<ArgumentNullException>("predicate", () => Assert.Single(new object[0], null!));
		}
 
		[Fact]
		public static void SingleMatch()
		{
			var collection = new[] { "Hello", "World" };
 
			var result = Assert.Single(collection, item => item.StartsWith("H"));
 
			Assert.Equal("Hello", result);
		}
 
		[Fact]
		public static void NoMatches()
		{
			var collection = new[] { "Hello", "World" };
 
			var ex = Record.Exception(() => Assert.Single(collection, item => false));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection did not contain any matching items" + Environment.NewLine +
				"Expected:   (predicate expression)" + Environment.NewLine +
				"Collection: [\"Hello\", \"World\"]",
				ex.Message
			);
		}
 
		[Fact]
		public static void TooManyMatches()
		{
			var collection = new[] { "Hello", "World" };
 
			var ex = Record.Exception(() => Assert.Single(collection, item => true));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection contained 2 matching items" + Environment.NewLine +
				"Expected:      (predicate expression)" + Environment.NewLine +
				"Collection:    [\"Hello\", \"World\"]" + Environment.NewLine +
				"Match indices: 0, 1",
				ex.Message
			);
		}
 
		[Fact]
		public static void Truncation()
		{
			var collection = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 4 };
 
			var ex = Record.Exception(() => Assert.Single(collection, item => item == 4));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection contained 2 matching items" + Environment.NewLine +
				"Expected:      (predicate expression)" + Environment.NewLine +
				$"Collection:    [1, 2, 3, 4, 5, {ArgumentFormatter.Ellipsis}]" + Environment.NewLine +
				"Match indices: 3, 8",
				ex.Message
			);
		}
 
		[Fact]
		public static void StringAsCollection_Match()
		{
			var collection = "H";
 
			var value = Assert.Single(collection, c => c != 'Q');
 
			Assert.Equal('H', value);
		}
 
		[Fact]
		public static void StringAsCollection_NoMatch()
		{
			var collection = "H";
 
			var ex = Record.Exception(() => Assert.Single(collection, c => c == 'Q'));
 
			Assert.IsType<SingleException>(ex);
			Assert.Equal(
				"Assert.Single() Failure: The collection did not contain any matching items" + Environment.NewLine +
				"Expected:   (predicate expression)" + Environment.NewLine +
				"Collection: ['H']",
				ex.Message
			);
		}
	}
 
	sealed class SpyEnumerator<T> : IEnumerable<T>, IEnumerator<T>
	{
		IEnumerator<T>? innerEnumerator;
 
		public SpyEnumerator(IEnumerable<T> enumerable)
		{
			innerEnumerator = enumerable.GetEnumerator();
		}
 
		public T Current =>
			GuardNotNull("Tried to get Current on a disposed enumerator", innerEnumerator).Current;
 
		object? IEnumerator.Current =>
			GuardNotNull("Tried to get Current on a disposed enumerator", innerEnumerator).Current;
 
		public bool IsDisposed => innerEnumerator is null;
 
		public IEnumerator<T> GetEnumerator() => this;
 
		IEnumerator IEnumerable.GetEnumerator() => this;
 
		public bool MoveNext() =>
			GuardNotNull("Tried to call MoveNext() on a disposed enumerator", innerEnumerator).MoveNext();
 
		public void Reset() => throw new NotImplementedException();
 
		public void Dispose()
		{
			innerEnumerator?.Dispose();
			innerEnumerator = null;
		}
 
		/// <summary/>
		static T2 GuardNotNull<T2>(
			string message,
			[NotNull] T2? value)
				where T2 : class
		{
			if (value is null)
				throw new InvalidOperationException(message);
 
			return value;
		}
	}
}