// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using AwesomeAssertions;

using TestFramework.ForTestingMSTest;

namespace Microsoft.VisualStudio.TestTools.UnitTesting.UnitTests;

/// <summary>
/// Unit tests for the <see cref="Assert"/> class and its nested <see cref="Assert.AssertSingleInterpolatedStringHandler{TItem}"/> struct.
/// </summary>
public partial class AssertTests : TestContainer
{
    #region Helper Methods

    /// <summary>
    /// Invokes the ComputeAssertion method on a handler and returns the exception message if one is thrown.
    /// </summary>
    /// <typeparam name="T">The type parameter.</typeparam>
    /// <param name="handler">The handler instance.</param>
    /// <returns>The exception message thrown by ComputeAssertion.</returns>
    private static string GetComputeAssertionExceptionMessage<T>(Assert.AssertSingleInterpolatedStringHandler<T> handler)
    {
        try
        {
            // This call is expected to throw when _builder is not null.
            _ = handler.ComputeAssertion("<collection arg expression placeholder>");
        }
        catch (Exception ex)
        {
            return ex.Message;
        }

        throw new Exception("Expected exception was not thrown.");
    }

    #endregion

    #region AssertSingleInterpolatedStringHandler and ComputeAssertion Tests

    /// <summary>
    /// Tests that ComputeAssertion returns the single element when the collection has exactly one element.
    /// </summary>
    public void ComputeAssertion_WhenCollectionHasSingleElement_ReturnsElement()
    {
        // Arrange
        int singleItem = 42;
        var collection = new List<int> { singleItem };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<int>(literalLength: 0, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeFalse();

        // Act
        int result = handler.ComputeAssertion("<collection arg expression placeholder>");

        // Assert
        result.Should().Be(singleItem);
    }

    /// <summary>
    /// Tests that ComputeAssertion throws an exception when the collection does not contain exactly one element.
    /// </summary>
    public void ComputeAssertion_WhenCollectionDoesNotHaveSingleElement_ThrowsException()
    {
        // Arrange: collection with multiple elements forces shouldAppend true.
        var collection = new List<int> { 1, 2 };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<int>(literalLength: 5, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert: verify that the exception message contains expected parts.
        exMsg.Should().Contain("ContainsSingle");
        exMsg.Should().Contain("2"); // actual count is 2.
    }

    #endregion

    #region AppendLiteral and AppendFormatted Tests

    /// <summary>
    /// Tests that AppendLiteral appends the given literal text which appears in the thrown message.
    /// </summary>
    public void AppendLiteral_WhenCalled_AppendsLiteralText()
    {
        // Arrange: use a collection with multiple elements to force using _builder.
        var collection = new List<string> { "a", "b" };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<string>(literalLength: 10, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();
        string literal = "LiteralText";
        handler.AppendLiteral(literal);

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert: the exception message should contain the literal appended.
        exMsg.Should().Contain(literal);
    }

    /// <summary>
    /// Tests the AppendFormatted overload that takes a generic value.
    /// </summary>
    public void AppendFormatted_GenericValue_WithoutFormat_AppendsValue()
    {
        // Arrange
        var collection = new List<string> { "x", "y" };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<string>(literalLength: 0, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();
        string value = "FormattedValue";
        handler.AppendFormatted(value);

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert
        exMsg.Should().Contain(value);
    }

    /// <summary>
    /// Tests the AppendFormatted overload with a generic value and a format string.
    /// </summary>
    public void AppendFormatted_GenericValue_WithFormat_AppendsFormattedValue()
    {
        // Arrange
        var collection = new List<int> { 1, 2 };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<int>(literalLength: 0, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();
        int value = 123;
        string format = "D5";
        handler.AppendFormatted(value, format);

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert: Check if the value was formatted accordingly ("00123")
        exMsg.Should().Contain("00123");
    }

    /// <summary>
    /// Tests the AppendFormatted overload with a generic value and an alignment.
    /// </summary>
    public void AppendFormatted_GenericValue_WithAlignment_AppendsAlignedValue()
    {
        // Arrange
        var collection = new List<double> { 0.1, 0.2 };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<double>(literalLength: 0, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();
        double value = 3.14;
        int alignment = 10;
        handler.AppendFormatted(value, alignment);

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert: alignment is applied via StringBuilder.AppendFormat so result should contain formatted spacing.
        exMsg.Should().Contain("3.14");
    }

    /// <summary>
    /// Tests the AppendFormatted overload with a generic value, alignment, and a format string.
    /// </summary>
    public void AppendFormatted_GenericValue_WithAlignmentAndFormat_AppendsFormattedValue()
    {
        // Arrange
        var collection = new List<DateTime> { DateTime.Now, DateTime.UtcNow };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<DateTime>(literalLength: 0, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();
        var value = new DateTime(2023, 1, 1);
        int alignment = 8;
        string format = "yyyy";
        handler.AppendFormatted(value, alignment, format);

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert: formatted year "2023" should appear.
        exMsg.Should().Contain("2023");
    }

    /// <summary>
    /// Tests the AppendFormatted overload that takes a string value.
    /// </summary>
    public void AppendFormatted_StringValue_WithoutAdditionalParameters_AppendsString()
    {
        // Arrange
        var collection = new List<string> { "first", "second" };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<string>(literalLength: 0, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();
        string value = "TestString";
        handler.AppendFormatted(value);

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert
        exMsg.Should().Contain(value);
    }

    /// <summary>
    /// Tests the AppendFormatted overload with a string value along with alignment and format.
    /// </summary>
    public void AppendFormatted_StringValue_WithAlignmentAndFormat_AppendsFormattedString()
    {
        // Arrange
        var collection = new List<string> { "one", "two" };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<string>(literalLength: 0, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();
        string value = "Hello";
        int alignment = 10;
        string format = "X"; // Though format for string doesn't change, we test the overload route.
        handler.AppendFormatted(value, alignment, format);

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert
        exMsg.Should().Contain(value);
    }

    /// <summary>
    /// Tests the AppendFormatted overload for object with alignment and format.
    /// </summary>
    public void AppendFormatted_ObjectValue_WithAlignmentAndFormat_AppendsFormattedObject()
    {
        // Arrange
        var collection = new List<object> { 1, 2 };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<object>(literalLength: 0, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();
        object value = 99;
        int alignment = 5;
        string format = "D3";
        handler.AppendFormatted(value, alignment, format);

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert: Formatted value should appear (e.g. "099").
        exMsg.Should().Contain("099");
    }

#if NETCOREAPP3_1_OR_GREATER
    /// <summary>
    /// Tests the AppendFormatted overload for ReadOnlySpan/<char/>.
    /// </summary>
    public void AppendFormatted_ReadOnlySpan_AppendsSpanValue()
    {
        // Arrange
        var collection = new List<string> { "alpha", "beta" };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<string>(literalLength: 0, formattedCount: 0, collection, out bool shouldAppend);
        shouldAppend.Should().BeTrue();
        ReadOnlySpan<char> spanValue = "SpanText".AsSpan();
        handler.AppendFormatted(spanValue);

        // Act
        string exMsg = GetComputeAssertionExceptionMessage(handler);

        // Assert
        exMsg.Should().Contain("SpanText");
    }
#endif

    #endregion

    #region ContainsSingle Tests

    /// <summary>
    /// Tests the ContainsSingle method without message parameters where the collection has a single element.
    /// </summary>
    public void ContainsSingle_NoMessage_WithSingleElement_ReturnsElement()
    {
        // Arrange
        var collection = new List<int> { 100 };

        // Act
        int result = Assert.ContainsSingle(collection);

        // Assert
        result.Should().Be(100);
    }

    /// <summary>
    /// Tests the ContainsSingle method without message parameters where the collection has a single element.
    /// </summary>
    public void ContainsSingle_InNonGenericCollection_NoMessage_WithSingleElement_ReturnsElement()
    {
        // Arrange
        var collection = new ArrayList { 100 };

        // Act
        object? result = Assert.ContainsSingle(collection);

        // Assert
        result.Should().Be(100);
    }

    /// <summary>
    /// Tests the ContainsSingle method with a message where the collection has a single element.
    /// </summary>
    public void ContainsSingle_WithMessage_WithSingleElement_ReturnsElement()
    {
        // Arrange
        var collection = new List<string> { "OnlyOne" };

        // Act
        string result = Assert.ContainsSingle(collection, "Custom message");

        // Assert
        result.Should().Be("OnlyOne");
    }

    /// <summary>
    /// Tests the ContainsSingle method with a message where the collection has a single element.
    /// </summary>
    public void ContainsSingle_InNonGenericCollection_WithMessage_WithSingleElement_ReturnsElement()
    {
        // Arrange
        var collection = new ArrayList { "OnlyOne" };

        // Act
        object? result = Assert.ContainsSingle(collection, "Custom message");

        // Assert
        result.Should().Be("OnlyOne");
    }

    /// <summary>
    /// Tests the ContainsSingle method that uses an interpolated string handler when the collection has multiple elements.
    /// Expects an exception.
    /// </summary>
    public void ContainsSingle_InterpolatedHandler_WithMultipleElements_ThrowsException()
    {
        // Arrange
        var collection = new List<int> { 1, 2, 3 };
        var handler = new Assert.AssertSingleInterpolatedStringHandler<int>(literalLength: 5, formattedCount: 0, collection, out bool dummy);

        // Act
        Action action = () => Assert.ContainsSingle(collection, ref handler);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to contain exactly one element.*expected count:*1*actual count:*3*Assert.ContainsSingle(collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method without message parameters where the collection has a single element.
    /// </summary>
    public void ContainsSingle_InNonGenericCollection_NoMessage_WithNull_ReturnsElement()
    {
        // Arrange
        var collection = new ArrayList { null };

        // Act
        object? result = Assert.ContainsSingle(collection);

        // Assert
        result.Should().Be(null);
    }

    /// <summary>
    /// Tests the ContainsSingle method without message parameters where the collection has a single element.
    /// </summary>
    public void ContainsSingle_InNonGenericCollection_NoMessage_WithEmptyCollection_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList();

        // Act
        Action action = () => Assert.ContainsSingle(collection);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to contain exactly one element.*expected count: 1*actual count:   0*Assert.ContainsSingle(collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method without message parameters where the collection has multiple elements.
    /// </summary>
    public void ContainsSingle_InNonGenericCollection_NoMessage_WithMultipleElements_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 1, 2, 3 };

        // Act
        Action action = () => Assert.ContainsSingle(collection);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to contain exactly one element.*expected count: 1*actual count:   3*Assert.ContainsSingle(collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with message parameter where the collection has a no element (empty collection).
    /// </summary>
    public void ContainsSingle_InNonGenericCollection_AssertCustomMessage_WithEmptyCollection_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList();

        // Act
        Action action = () => Assert.ContainsSingle(collection, "my custom message");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to contain exactly one element.*my custom message*expected count: 1*actual count:   0*Assert.ContainsSingle(collection)*");
    }
    #endregion

    #region Contains Tests

    /// <summary>
    /// Tests the Contains method (value overload) when the expected item is present.
    /// </summary>
    public void Contains_ValueExpected_ItemExists_DoesNotThrow()
    {
        // Arrange
        var collection = new List<int> { 5, 10, 15 };

        // Act
        Action action = () => Assert.Contains(10, collection, "No failure expected");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the Contains method (value overload) when the expected item is not present.
    /// Expects an exception.
    /// </summary>
    public void Contains_ValueExpected_ItemDoesNotExist_ThrowsException()
    {
        // Arrange
        var collection = new List<int> { 5, 10, 15 };

        // Act
        Action action = () => Assert.Contains(20, collection, "Item 20 not found");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to contain the specified element.*Item 20 not found*expected:*Assert.Contains(20, collection)*");
    }

    /// <summary>
    /// Tests the Contains method (value overload) when the expected item is present.
    /// </summary>
    public void Contains_InNonGenericCollection_ValueExpected_ItemDoesNotExist_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 5, 10, "a" };
        object expected = 20;

        // Act
        Action action = () => Assert.Contains(expected, collection, $"Item {expected} not found");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage($"*Item {expected} not found*");
    }

    /// <summary>
    /// Tests the Contains method (value overload) when the expected item is present.
    /// </summary>
    public void Contains_InNonGenericCollection_ValueExpected_ItemExists_DoesNotThrow()
    {
        // Arrange
        var collection = new ArrayList { 5, 10, "a" };

        // Act
        Action action = () => Assert.Contains("a", collection, "No failure expected");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the Contains method (value overload) when the expected item is present.
    /// </summary>
    public void Contains_InNonGenericCollection_ValueExpected_ItemExists_DoesNotThrowException()
    {
        // Arrange
        var collection = new ArrayList { 5, 10, "a" };

        // Act
        Action action = () => Assert.Contains(5, collection);

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the Contains method (value overload) when the expected item is not present.
    /// </summary>
    public void Contains_InNonGenericCollection_NullableValueExpected_ItemDoesNotExist_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 5, 10, "a" };
        object? expected = null;

        // Act
        Action action = () => Assert.Contains(expected, collection, $"Item {expected} not found");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage($"*Item {expected} not found*");
    }

    /// <summary>
    /// Tests the Contains method (value overload) when the expected item is present.
    /// </summary>
    public void Contains_InNonGenericCollection_NullableValueExpected_ItemExists_DoesNotThrow()
    {
        // Arrange
        var collection = new ArrayList { null, 10, "a" };
        object? expected = null;

        // Act
        Action action = () => Assert.Contains(expected, collection, "No failure expected");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the Contains method with a comparer when the expected item is present.
    /// </summary>
    public void Contains_WithComparer_ItemExists_DoesNotThrow()
    {
        // Arrange
        var collection = new List<string> { "apple", "banana" };
        IEqualityComparer<string> comparer = StringComparer.OrdinalIgnoreCase;

        // Act
        Action action = () => Assert.Contains("APPLE", collection, comparer, "Should find apple");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the Contains method with a comparer when the expected item is not present.
    /// Expects an exception.
    /// </summary>
    public void Contains_WithComparer_ItemDoesNotExist_ThrowsException()
    {
        // Arrange
        var collection = new List<string> { "apple", "banana" };
        IEqualityComparer<string> comparer = StringComparer.OrdinalIgnoreCase;

        // Act
        Action action = () => Assert.Contains("cherry", collection, comparer, "Missing cherry");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to contain the specified element.*Missing cherry*expected:*Assert.Contains(\"cherry\", collection)*");
    }

    /// <summary>
    /// Tests the Contains method that accepts a predicate when an element satisfies the condition.
    /// </summary>
    public void Contains_Predicate_ItemMatches_DoesNotThrow()
    {
        // Arrange
        var collection = new List<int> { 2, 4, 6 };

        // Act
        Action action = () => Assert.Contains(IsEven, collection, "Even number exists");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the Contains method that accepts a predicate when an element satisfies the condition.
    /// </summary>
    public void Contains_InNonGenericCollection_Predicate_ItemMatches_DoesNotThrow()
    {
        // Arrange
        var collection = new ArrayList { 2, 4, 6, "a" };

        // Act
        Action action = () => Assert.Contains(IsEven, collection, "Even number exists");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the Contains method that accepts a predicate when no element satisfies the condition.
    /// Expects an exception.
    /// </summary>
    public void Contains_Predicate_NoItemMatches_ThrowsException()
    {
        // Arrange
        var collection = new List<int> { 1, 3, 5 };

        // Act
        Action action = () => Assert.Contains(IsEven, collection, "No even number found");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to contain an element matching the predicate.*No even number found*Assert.Contains(IsEven, collection)*");
    }

    /// <summary>
    /// Tests the Contains method that accepts a predicate when no element satisfies the condition.
    /// Expects an exception.
    /// </summary>
    public void Contains_InNonGenericCollection_Predicate_NoItemMatches_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 1, 3, 5, "a" };

        // Act
        Action action = () => Assert.Contains(IsEven, collection, "No even number found");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to contain an element matching the predicate.*No even number found*Assert.Contains(IsEven, collection)*");
    }

    /// <summary>
    /// Tests the string Contains overload when the substring is contained in the value.
    /// </summary>
    public void Contains_StringVersion_SubstringPresent_DoesNotThrow()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "brown";

        // Act
        Action action = () => Assert.Contains(substring, value, StringComparison.Ordinal, "Substring found");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the string Contains overload when the substring is not contained in the value.
    /// Expects an exception.
    /// </summary>
    public void Contains_StringVersion_SubstringNotPresent_ThrowsException()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "lazy";

        // Act
        Action action = () => Assert.Contains(substring, value, StringComparison.Ordinal, "Missing substring");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected string to contain the specified substring.*Missing substring*expected substring: \"lazy\"*actual:*\"The quick brown fox\"*Assert.Contains(substring, value)*");
    }

    /// <summary>
    /// Tests the string Contains overload with StringComparison and message when substring is present.
    /// </summary>
    public void Contains_StringWithComparisonAndMessage_SubstringPresent_DoesNotThrow()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "BROWN";

        // Act
        Action action = () => Assert.Contains(substring, value, StringComparison.OrdinalIgnoreCase, "Substring found");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    public void Contains_HashSetWithCustomComparer_ItemExists_DoesNotThrow()
    {
        var collection = new HashSet<string>(AlwaysTrueEqualityComparer.Instance) { "1" };

        // This call shouldn't use EqualityComparer<string>.Default.
        Action action = () => Assert.Contains("2", collection);
        action.Should().NotThrow<AssertFailedException>();
        action();
    }

    /// <summary>
    /// Tests the Contains method (non-generic) when collection is null.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_InNonGenericCollection_NullCollection_ThrowsException()
    {
        // Arrange
        IEnumerable collection = null!;

        // Act
        Action action = () => Assert.Contains(1, collection);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'collection' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the Contains method (non-generic) with comparer when collection is null.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_InNonGenericCollection_WithComparer_NullCollection_ThrowsException()
    {
        // Arrange
        IEnumerable collection = null!;
        EqualityComparer<object> comparer = EqualityComparer<object>.Default;

        // Act
        Action action = () => Assert.Contains(1, collection, comparer);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'collection' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the Contains method (non-generic) with comparer when item exists.
    /// </summary>
    public void Contains_InNonGenericCollection_WithComparer_ItemExists_DoesNotThrow()
    {
        // Arrange
        var collection = new ArrayList { "apple", "BANANA", "cherry" };
        StringComparer comparer = StringComparer.OrdinalIgnoreCase;

        // Act
        Action action = () => Assert.Contains("banana", collection, comparer);

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the Contains method (non-generic) with comparer when item does not exist.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_InNonGenericCollection_WithComparer_ItemDoesNotExist_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { "apple", "cherry" };
        StringComparer comparer = StringComparer.OrdinalIgnoreCase;

        // Act
        Action action = () => Assert.Contains("banana", collection, comparer);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to contain the specified element.*expected:*Assert.Contains(\"banana\", collection)*");
    }

    /// <summary>
    /// Tests the Contains method (non-generic) with predicate when collection is null.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_InNonGenericCollection_Predicate_NullCollection_ThrowsException()
    {
        // Arrange
        IEnumerable collection = null!;

        // Act
        Action action = () => Assert.Contains(Predicate, collection);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'collection' is invalid. The value cannot be null.");

        // Local functions
        static bool Predicate(object? x) => x is int i && i > 5;
    }

    /// <summary>
    /// Tests the Contains method (non-generic) with null predicate.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_InNonGenericCollection_NullPredicate_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 1, 2, 3 };
        Func<object?, bool> predicate = null!;

        // Act
        Action action = () => Assert.Contains(predicate, collection);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'predicate' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the Contains method (non-generic) with collection containing multiple nulls when searching for null.
    /// </summary>
    public void Contains_InNonGenericCollection_MultipleNulls_SearchingForNull_DoesNotThrow()
    {
        // Arrange
        var collection = new ArrayList { null, 1, null, 2 };

        // Act
        Action action = () => Assert.Contains(x => x is null, collection);

        // Assert
        action.Should().NotThrow();
    }

    /// <summary>
    /// Tests the string Contains method when substring is null.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_String_NullSubstring_ThrowsException()
    {
        // Arrange
        string substring = null!;
        string value = "test value";

        // Act
        Action action = () => Assert.Contains(substring, value);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'substring' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the string Contains method when value is null.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_String_NullValue_ThrowsException()
    {
        // Arrange
        string substring = "test";
        string value = null!;

        // Act
        Action action = () => Assert.Contains(substring, value);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'value' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the string Contains method when both substring and value are null.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_String_BothNull_ThrowsException()
    {
        // Arrange
        string substring = null!;
        string value = null!;

        // Act
        Action action = () => Assert.Contains(substring, value);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'value' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the string Contains method with empty substring.
    /// </summary>
    public void Contains_String_EmptySubstring_DoesNotThrow()
    {
        // Arrange
        string substring = string.Empty;
        string value = "test value";

        // Act
        Action action = () => Assert.Contains(substring, value);

        // Assert
        // Empty string is contained in any string
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the string Contains method with empty value.
    /// </summary>
    public void Contains_String_EmptyValue_WithNonEmptySubstring_ThrowsException()
    {
        // Arrange
        string substring = "test";
        string value = string.Empty;

        // Act
        Action action = () => Assert.Contains(substring, value);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected string to contain the specified substring.*expected substring: \"test\"*actual:*\"\"*Assert.Contains(substring, value)*");
    }

    /// <summary>
    /// Tests the string Contains method with both empty strings.
    /// </summary>
    public void Contains_String_BothEmpty_DoesNotThrow()
    {
        // Arrange
        string substring = string.Empty;
        string value = string.Empty;

        // Act
        Action action = () => Assert.Contains(substring, value);

        // Assert
        // Empty string contains empty string
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the string Contains method with StringComparison when substring is null.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_String_WithComparison_NullSubstring_ThrowsException()
    {
        // Arrange
        string substring = null!;
        string value = "test value";

        // Act
        Action action = () => Assert.Contains(substring, value, StringComparison.OrdinalIgnoreCase);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'substring' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the string Contains method with StringComparison when value is null.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_String_WithComparison_NullValue_ThrowsException()
    {
        // Arrange
        string substring = "test";
        string value = null!;

        // Act
        Action action = () => Assert.Contains(substring, value, StringComparison.OrdinalIgnoreCase);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'value' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the Contains method (non-generic) with null comparer.
    /// Expects an AssertFailedException.
    /// </summary>
    public void Contains_InNonGenericCollection_WithComparer_NullComparer_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { "apple", "banana" };
        IEqualityComparer comparer = null!;

        // Act
        Action action = () => Assert.Contains("apple", collection, comparer);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.Contains failed. The parameter 'comparer' is invalid. The value cannot be null.");
    }

    #endregion

    #region DoesNotContain Tests

    /// <summary>
    /// Tests the DoesNotContain method (value overload) when the expected item is not present.
    /// </summary>
    public void DoesNotContain_ValueExpected_ItemNotPresent_DoesNotThrow()
    {
        // Arrange
        var collection = new List<int> { 5, 10, 15 };

        // Act
        Action action = () => Assert.DoesNotContain(20, collection, "No failure expected");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the DoesNotContain method (value overload) when the expected item is present.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_ValueExpected_ItemPresent_ThrowsException()
    {
        // Arrange
        var collection = new List<int> { 5, 10, 15 };

        // Act
        Action action = () => Assert.DoesNotContain(10, collection, "Item 10 should not be found");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to not contain the specified element.*Item 10 should not be found*unexpected:*Assert.DoesNotContain(10, collection)*");
    }

    /// <summary>
    /// Tests the DoesNotContain method (value overload) when the expected item is not present.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_ValueExpected_ItemNotPresent_DoesNotThrow()
    {
        // Arrange
        var collection = new ArrayList { 5, 10, 15 };

        // Act
        Action action = () => Assert.DoesNotContain(20, collection, "No failure expected");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the DoesNotContain method (value overload) when the expected item is present.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_ValueExpected_ItemPresent_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 5, 10, 15, "a" };

        // Act
        Action action = () => Assert.DoesNotContain(10, collection, "Assert.DoesNotContain failed. Expected collection to not contain the specified item. Item {0} should not be found");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to not contain the specified element.*Assert.DoesNotContain failed. Expected collection to not contain the specified item. Item {0} should not be found*unexpected:*Assert.DoesNotContain(10, collection)*");
    }

    /// <summary>
    /// Tests the DoesNotContain method with a comparer when the item is not present.
    /// </summary>
    public void DoesNotContain_WithComparer_ItemNotPresent_DoesNotThrow()
    {
        // Arrange
        var collection = new List<string> { "apple", "banana" };
        IEqualityComparer<string> comparer = StringComparer.OrdinalIgnoreCase;

        // Act
        Action action = () => Assert.DoesNotContain("cherry", collection, comparer, "No cherry found");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the DoesNotContain method with a comparer when the item is not present.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_WithComparer_ItemNotPresent_DoesNotThrow()
    {
        // Arrange
        var collection = new ArrayList { "apple", "banana", 1 };
        IEqualityComparer comparer = StringComparer.OrdinalIgnoreCase;

        // Act
        Action action = () => Assert.DoesNotContain("cherry", collection, comparer, "No cherry found");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the DoesNotContain method with a comparer when the item is present.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_WithComparer_ItemPresent_ThrowsException()
    {
        // Arrange
        var collection = new List<string> { "apple", "banana" };
        IEqualityComparer<string> comparer = StringComparer.OrdinalIgnoreCase;

        // Act
        Action action = () => Assert.DoesNotContain("APPLE", collection, comparer, "Unexpected \"APPLE\"");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to not contain the specified element.*Unexpected \"APPLE\"*unexpected:*Assert.DoesNotContain(\"APPLE\", collection)*");
    }

    /// <summary>
    /// Tests the DoesNotContain method with a comparer when the item is present.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_WithComparer_ItemPresent_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { "apple", "banana", 1 };
        IEqualityComparer comparer = StringComparer.OrdinalIgnoreCase;

        // Act
        Action action = () => Assert.DoesNotContain("APPLE", collection, comparer, "APPLE");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to not contain the specified element.*APPLE*unexpected:*Assert.DoesNotContain(\"APPLE\", collection)*");
    }

    /// <summary>
    /// Tests the DoesNotContain method that accepts a predicate when no element satisfies the predicate.
    /// </summary>
    public void DoesNotContain_Predicate_NoItemMatches_DoesNotThrow()
    {
        // Arrange
        var collection = new List<int> { 1, 3, 5 };

        // Act
        Action action = () => Assert.DoesNotContain(IsEven, collection, "All items are odd");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the DoesNotContain method that accepts a predicate when no element satisfies the predicate.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_Predicate_NoItemMatches_DoesNotThrow()
    {
        // Arrange
        var collection = new ArrayList { 1, 3, 5, "a" };

        // Act
        Action action = () => Assert.DoesNotContain(IsEven, collection, "All items are odd");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the DoesNotContain method that accepts a predicate when at least one element satisfies the predicate.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_Predicate_AtLeastOneItemMatches_ThrowsException()
    {
        // Arrange
        var collection = new List<int> { 2, 3, 5 };

        // Act
        Action action = () => Assert.DoesNotContain(IsEven, collection, "An even number exists");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to not contain an element matching the predicate.*An even number exists*Assert.DoesNotContain(IsEven, collection)*");
    }

    /// <summary>
    /// Tests the DoesNotContain method that accepts a predicate when at least one element satisfies the predicate.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_Predicate_AtLeastOneItemMatches_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 2, 3, 5, "a" };

        // Act
        Action action = () => Assert.DoesNotContain(IsEven, collection, "An even number exists");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected collection to not contain an element matching the predicate.*An even number exists*Assert.DoesNotContain(IsEven, collection)*");
    }

    /// <summary>
    /// Tests the string DoesNotContain overload when the substring is not contained in the value.
    /// </summary>
    public void DoesNotContain_StringVersion_SubstringNotPresent_DoesNotThrow()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "lazy";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value, StringComparison.Ordinal, "Should not contain");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the string DoesNotContain overload when the substring is contained in the value.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_StringVersion_SubstringPresent_ThrowsException()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "brown";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value, StringComparison.Ordinal, "Unexpected substring");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected string to not contain the specified substring*Unexpected substring*unexpected substring: \"brown\"*actual:*\"The quick brown fox\"*Assert.DoesNotContain(substring, value)*");
    }

    /// <summary>
    /// Tests the string DoesNotContain overload with StringComparison and message when substring is not present.
    /// This test ensures the method overload works correctly and prevents regression of stackoverflow bug.
    /// </summary>
    public void DoesNotContain_StringWithComparisonAndMessage_SubstringNotPresent_DoesNotThrow()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "LAZY";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value, StringComparison.OrdinalIgnoreCase, "Should not contain lazy");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the string DoesNotContain overload with StringComparison and message when substring is present.
    /// This test ensures the method overload works correctly and prevents regression of stackoverflow bug.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_StringWithComparisonAndMessage_SubstringPresent_ThrowsException()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "BROWN";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value, StringComparison.OrdinalIgnoreCase, "Found unexpected substring");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected string to not contain the specified substring.*Found unexpected substring*unexpected substring: \"BROWN\"*actual:*\"The quick brown fox\"*Assert.DoesNotContain(substring, value)*");
    }

    /// <summary>
    /// Tests the simplest string DoesNotContain overload when substring is not present.
    /// </summary>
    public void DoesNotContain_StringSimpleOverload_SubstringNotPresent_DoesNotThrow()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "lazy";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value);

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the simplest string DoesNotContain overload when substring is present.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_StringSimpleOverload_SubstringPresent_ThrowsException()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "brown";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected string to not contain the specified substring.*unexpected substring: \"brown\"*actual:*\"The quick brown fox\"*Assert.DoesNotContain(substring, value)*");
    }

    /// <summary>
    /// Tests the string DoesNotContain overload with message only when substring is not present.
    /// </summary>
    public void DoesNotContain_StringWithMessageOnly_SubstringNotPresent_DoesNotThrow()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "lazy";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value, "Should not contain lazy");

        // Assert
        action.Should().NotThrow<AssertFailedException>();
    }

    /// <summary>
    /// Tests the string DoesNotContain overload with message only when substring is present.
    /// Expects an exception.
    /// </summary>
    public void DoesNotContain_StringWithMessageOnly_SubstringPresent_ThrowsException()
    {
        // Arrange
        string value = "The quick brown fox";
        string substring = "brown";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value, "Found unexpected substring");

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("*Expected string to not contain the specified substring.*Found unexpected substring*unexpected substring: \"brown\"*actual:*\"The quick brown fox\"*Assert.DoesNotContain(substring, value)*");
    }

    /// <summary>
    /// Tests the DoesNotContain method (non-generic) when collection is null.
    /// Expects an AssertFailedException.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_NullCollection_ThrowsException()
    {
        // Arrange
        IEnumerable collection = null!;

        // Act
        Action action = () => Assert.DoesNotContain(1, collection);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.DoesNotContain failed. The parameter 'collection' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the DoesNotContain method (non-generic) with comparer when collection is null.
    /// Expects an ArgumentNullException.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_WithComparer_NullCollection_ThrowsException()
    {
        // Arrange
        IEnumerable collection = null!;
        EqualityComparer<object> comparer = EqualityComparer<object>.Default;

        // Act
        Action action = () => Assert.DoesNotContain(1, collection, comparer);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.DoesNotContain failed. The parameter 'collection' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the DoesNotContain method (non-generic) with null comparer.
    /// Expects an ArgumentNullException.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_WithComparer_NullComparer_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { "apple", "banana" };
        IEqualityComparer comparer = null!;

        // Act
        Action action = () => Assert.DoesNotContain("cherry", collection, comparer);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.DoesNotContain failed. The parameter 'comparer' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the DoesNotContain method (non-generic) with predicate when collection is null.
    /// Expects an ArgumentNullException.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_Predicate_NullCollection_ThrowsException()
    {
        // Arrange
        IEnumerable collection = null!;

        // Act
        Action action = () => Assert.DoesNotContain(Predicate, collection);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.DoesNotContain failed. The parameter 'collection' is invalid. The value cannot be null.");

        // Local functions
        static bool Predicate(object? x) => x is int i && i > 5;
    }

    /// <summary>
    /// Tests the DoesNotContain method (non-generic) with null predicate.
    /// Expects an ArgumentNullException.
    /// </summary>
    public void DoesNotContain_InNonGenericCollection_NullPredicate_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 1, 2, 3 };
        Func<object?, bool> predicate = null!;

        // Act
        Action action = () => Assert.DoesNotContain(predicate, collection);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.DoesNotContain failed. The parameter 'predicate' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the string DoesNotContain method when substring is null.
    /// Expects an ArgumentNullException.
    /// </summary>
    public void DoesNotContain_String_NullSubstring_ThrowsException()
    {
        // Arrange
        string substring = null!;
        string value = "test value";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.DoesNotContain failed. The parameter 'substring' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the string DoesNotContain method when value is null.
    /// Expects an ArgumentNullException.
    /// </summary>
    public void DoesNotContain_String_NullValue_ThrowsException()
    {
        // Arrange
        string substring = "test";
        string value = null!;

        // Act
        Action action = () => Assert.DoesNotContain(substring, value);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.DoesNotContain failed. The parameter 'value' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the string DoesNotContain method with StringComparison when substring is null.
    /// Expects an ArgumentNullException.
    /// </summary>
    public void DoesNotContain_String_WithComparison_NullSubstring_ThrowsException()
    {
        // Arrange
        string substring = null!;
        string value = "test value";

        // Act
        Action action = () => Assert.DoesNotContain(substring, value, StringComparison.OrdinalIgnoreCase);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.DoesNotContain failed. The parameter 'substring' is invalid. The value cannot be null.");
    }

    /// <summary>
    /// Tests the string DoesNotContain method with StringComparison when value is null.
    /// Expects an ArgumentNullException.
    /// </summary>
    public void DoesNotContain_String_WithComparison_NullValue_ThrowsException()
    {
        // Arrange
        string substring = "test";
        string value = null!;

        // Act
        Action action = () => Assert.DoesNotContain(substring, value, StringComparison.OrdinalIgnoreCase);

        // Assert
        action.Should().Throw<AssertFailedException>().WithMessage("Assert.DoesNotContain failed. The parameter 'value' is invalid. The value cannot be null.");
    }

    private static bool IsEven(int x) => x % 2 == 0;

    private static bool IsEven(object? x) => x is int i && i % 2 == 0;

    #endregion

    #region ContainsSingle with Predicate Tests

    /// <summary>
    /// Tests the ContainsSingle method with predicate when exactly one element matches.
    /// </summary>
    public void ContainsSinglePredicate_NoMessage_OneItemMatches_ReturnsElement()
    {
        // Arrange
        var collection = new List<int> { 1, 2, 3, 4, 5 };

        // Act
        int result = Assert.ContainsSingle(x => x == 3, collection);

        // Assert
        result.Should().Be(3);
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate when exactly one element matches in non-generic collection.
    /// </summary>
    public void ContainsSinglePredicate_InNonGenericCollection_NoMessage_OneItemMatches_ReturnsElement()
    {
        // Arrange
        var collection = new ArrayList { 1, 2, 3, 4, 5, "a" };

        // Act
        object? result = Assert.ContainsSingle(x => x!.Equals(3), collection);

        // Assert
        result.Should().Be(3);
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate and message when exactly one element matches.
    /// </summary>
    public void ContainsSinglePredicate_WithMessage_OneItemMatches_ReturnsElement()
    {
        // Arrange
        var collection = new List<string> { "apple", "banana", "cherry" };

        // Act
#pragma warning disable CA1865 // Use char overload - not netfx
        string result = Assert.ContainsSingle(x => x.StartsWith("b", StringComparison.Ordinal), collection, "Expected one item starting with 'b'");
#pragma warning restore CA1865 // Use char overload

        // Assert
        result.Should().Be("banana");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate and message when exactly one element matches in non-generic collection.
    /// </summary>
    public void ContainsSinglePredicate_InNonGenericCollection_WithMessage_OneItemMatches_ReturnsElement()
    {
        // Arrange
        var collection = new ArrayList { "apple", "banana", "cherry" };

        // Act
#pragma warning disable CA1865 // Use char overload - not netfx
        object? result = Assert.ContainsSingle(x => x is string s && s.StartsWith("b", StringComparison.Ordinal), collection, "Expected one item starting with 'b'");
#pragma warning restore CA1865 // Use char overload

        // Assert
        result.Should().Be("banana");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate when no elements match.
    /// Expects an exception.
    /// </summary>
    public void ContainsSinglePredicate_NoItemMatches_ThrowsException()
    {
        // Arrange
        var collection = new List<int> { 1, 3, 5 };

        // Act
        Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*expected matches: 1*actual matches:   0*Assert.ContainsSingle(x => x % 2 == 0, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate when no elements match in non-generic collection.
    /// Expects an exception.
    /// </summary>
    public void ContainsSinglePredicate_InNonGenericCollection_NoItemMatches_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 1, 3, 5, "a" };

        // Act
        Action action = () => Assert.ContainsSingle(x => x is int i && i % 2 == 0, collection);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*expected matches: 1*actual matches:   0*Assert.ContainsSingle(x => x is int i && i % 2 == 0, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate when the predicate expression is unavailable.
    /// Expects a predicate-specific exception using placeholders.
    /// </summary>
    public void ContainsSinglePredicate_EmptyPredicateExpression_UsesPredicateFailureMessage()
    {
        // Arrange
        var collection = new List<int> { 1, 3, 5 };

        // Act
        Action action = () => Assert.ContainsSingle(static x => x % 2 == 0, collection, predicateExpression: string.Empty);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*expected matches: 1*actual matches:   0*Assert.ContainsSingle(<predicate>, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate when the predicate expression is unavailable in non-generic collections.
    /// Expects a predicate-specific exception using placeholders.
    /// </summary>
    public void ContainsSinglePredicate_InNonGenericCollection_EmptyPredicateExpression_UsesPredicateFailureMessage()
    {
        // Arrange
        var collection = new ArrayList { 1, 3, 5, "a" };

        // Act
        Action action = () => Assert.ContainsSingle(static x => x is int i && i % 2 == 0, collection, predicateExpression: string.Empty);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*expected matches: 1*actual matches:   0*Assert.ContainsSingle(<predicate>, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate when multiple elements match.
    /// Expects an exception.
    /// </summary>
    public void ContainsSinglePredicate_MultipleItemsMatch_ThrowsException()
    {
        // Arrange
        var collection = new List<int> { 2, 4, 6, 8 };

        // Act
        Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*expected matches: 1*actual matches:   4*Assert.ContainsSingle(x => x % 2 == 0, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate when multiple elements match in non-generic collection.
    /// Expects an exception.
    /// </summary>
    public void ContainsSinglePredicate_InNonGenericCollection_MultipleItemsMatch_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 2, 4, 6, 8, "a" };

        // Act
        Action action = () => Assert.ContainsSingle(x => x is int i && i % 2 == 0, collection);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*expected matches: 1*actual matches:   4*Assert.ContainsSingle(x => x is int i && i % 2 == 0, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate and formatted message when no elements match.
    /// Expects an exception with the custom message.
    /// </summary>
    public void ContainsSinglePredicate_WithMessage_NoItemMatches_ThrowsException()
    {
        // Arrange
        var collection = new List<int> { 1, 3, 5 };

        // Act
        Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection, $"No even numbers found in collection with {collection.Count} items");

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*No even numbers found in collection with 3 items*expected matches: 1*actual matches:   0*Assert.ContainsSingle(x => x % 2 == 0, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate and formatted message when no elements match in non-generic collection.
    /// Expects an exception with the custom message.
    /// </summary>
    public void ContainsSinglePredicate_InNonGenericCollection_WithMessage_NoItemMatches_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 1, 3, 5 };

        // Act
        Action action = () => Assert.ContainsSingle(x => x is int i && i % 2 == 0, collection, $"No even numbers found in collection with {collection.Count} items");

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*No even numbers found in collection with 3 items*expected matches: 1*actual matches:   0*Assert.ContainsSingle(x => x is int i && i % 2 == 0, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate and formatted message when multiple elements match.
    /// Expects an exception with the custom message.
    /// </summary>
    public void ContainsSinglePredicate_WithMessage_MultipleItemsMatch_ThrowsException()
    {
        // Arrange
        var collection = new List<int> { 2, 4, 6 };

        // Act
        Action action = () => Assert.ContainsSingle(x => x % 2 == 0, collection, $"Too many even numbers found: {collection.Count}");

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*Too many even numbers found: 3*expected matches: 1*actual matches:   3*Assert.ContainsSingle(x => x % 2 == 0, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate and formatted message when multiple elements match in non-generic collection.
    /// Expects an exception with the custom message.
    /// </summary>
    public void ContainsSinglePredicate_InNonGenericCollection_WithMessage_MultipleItemsMatch_ThrowsException()
    {
        // Arrange
        var collection = new ArrayList { 2, 4, "a" };

        // Act
        Action action = () => Assert.ContainsSingle(x => x is int i && i % 2 == 0, collection, "Too many even numbers found: 2");

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain exactly one element matching the predicate.*Too many even numbers found: 2*expected matches: 1*actual matches:   2*Assert.ContainsSingle(x => x is int i && i % 2 == 0, collection)*");
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate using complex objects.
    /// </summary>
    public void ContainsSinglePredicate_ComplexObjects_OneItemMatches_ReturnsElement()
    {
        // Arrange
        var items = new List<Person>
        {
            new("Alice", 25),
            new("Bob", 30),
            new("Charlie", 35),
        };

        // Act
        Person result = Assert.ContainsSingle(p => p.Age == 30, items);

        // Assert
        result.Name.Should().Be("Bob");
        result.Age.Should().Be(30);
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate using complex objects in non-generic collection.
    /// </summary>
    public void ContainsSinglePredicate_InNonGenericCollection_ComplexObjects_OneItemMatches_ReturnsElement()
    {
        // Arrange
        var items = new ArrayList
        {
            new Person("Alice", 25),
            new Person("Bob", 30),
            new Person("Charlie", 35),
        };

        // Act
        object? result = Assert.ContainsSingle(p => p is Person person && person.Age == 30, items);

        // Assert
        result.Should().BeOfType<Person>();
        var resultPerson = (Person)result;
        resultPerson.Name.Should().Be("Bob");
        resultPerson.Age.Should().Be(30);
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate using null values.
    /// </summary>
    public void ContainsSinglePredicate_WithNullValues_OneItemMatches_ReturnsElement()
    {
        // Arrange
        var collection = new List<string?> { "apple", null, "banana" };

        // Act
        string? result = Assert.ContainsSingle(x => x == null, collection);

        // Assert
        result.Should().BeNull();
    }

    /// <summary>
    /// Tests the ContainsSingle method with predicate using null values in non-generic collection.
    /// </summary>
    public void ContainsSinglePredicate_InNonGenericCollection_WithNullValues_OneItemMatches_ReturnsElement()
    {
        // Arrange
        var collection = new ArrayList { "apple", null, "banana" };

        // Act
        object? result = Assert.ContainsSingle(x => x == null, collection);

        // Assert
        result.Should().BeNull();
    }

    #region New Error Message Tests

    /// <summary>
    /// Tests that Contains (item) failure shows specific error message.
    /// </summary>
    public void Contains_ItemNotFound_ShowsSpecificErrorMessage()
    {
        // Arrange
        var collection = new List<int> { 1, 2, 3 };

        // Act
        Action action = () => Assert.Contains(5, collection);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain the specified element.*expected:*Assert.Contains(5, collection)*");
    }

    /// <summary>
    /// Tests that Contains (item) failure shows specific error message.
    /// </summary>
    public void Contains_InNonGenericCollection_ItemNotFound_ShowsSpecificErrorMessage()
    {
        // Arrange
        var collection = new ArrayList { 1, 2, 3 };

        // Act
        Action action = () => Assert.Contains(5, collection);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain the specified element.*expected:*Assert.Contains(5, collection)*");
    }

    /// <summary>
    /// Tests that Contains (predicate) failure shows specific error message.
    /// </summary>
    public void Contains_PredicateNotMatched_ShowsSpecificErrorMessage()
    {
        // Arrange
        var collection = new List<int> { 1, 3, 5 };

        // Act
        Action action = () => Assert.Contains(x => x % 2 == 0, collection);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to contain an element matching the predicate.*Assert.Contains(x => x % 2 == 0, collection)*");
    }

    /// <summary>
    /// Tests that DoesNotContain (item) failure shows specific error message.
    /// </summary>
    public void DoesNotContain_ItemFound_ShowsSpecificErrorMessage()
    {
        // Arrange
        var collection = new List<int> { 1, 2, 3 };

        // Act
        Action action = () => Assert.DoesNotContain(2, collection);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to not contain the specified element.*unexpected:*Assert.DoesNotContain(2, collection)*");
    }

    /// <summary>
    /// Tests that DoesNotContain (predicate) failure shows specific error message.
    /// </summary>
    public void DoesNotContain_PredicateMatched_ShowsSpecificErrorMessage()
    {
        // Arrange
        var collection = new List<int> { 1, 2, 3 };

        // Act
        Action action = () => Assert.DoesNotContain(x => x % 2 == 0, collection);

        // Assert
        action.Should().Throw<AssertFailedException>()
            .WithMessage("*Expected collection to not contain an element matching the predicate.*Assert.DoesNotContain(x => x % 2 == 0, collection)*");
    }

    public void DoesNotContains_HashSetWithCustomComparer_ItemDoesNotExist_DoesNotThrow()
    {
        var collection = new HashSet<string>(AlwaysFalseEqualityComparer.Instance) { "1" };

        // This call shouldn't use EqualityComparer<string>.Default.
        Action action = () => Assert.DoesNotContain("1", collection);
        action.Should().NotThrow<AssertFailedException>();
        action();
    }

    #endregion

    private record Person(string Name, int Age);

    private sealed class AlwaysTrueEqualityComparer : IEqualityComparer<string>
    {
        private AlwaysTrueEqualityComparer()
        {
        }

        public static AlwaysTrueEqualityComparer Instance { get; } = new();

        public bool Equals(string? x, string? y) => true;

        public int GetHashCode([DisallowNull] string obj) => 0;
    }

    private sealed class AlwaysFalseEqualityComparer : IEqualityComparer<string>
    {
        private AlwaysFalseEqualityComparer()
        {
        }

        public static AlwaysFalseEqualityComparer Instance { get; } = new();

        public bool Equals(string? x, string? y) => false;

        public int GetHashCode([DisallowNull] string obj) => 0;
    }

    #endregion
}
