Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion TUnit.Assertions/Conditions/HasDistinctItemsAssertion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,22 @@ namespace TUnit.Assertions.Conditions;
public class HasDistinctItemsAssertion<TCollection, TItem> : Sources.CollectionAssertionBase<TCollection, TItem>
where TCollection : IEnumerable<TItem>
{
private readonly IEqualityComparer<TItem>? _comparer;

public HasDistinctItemsAssertion(
AssertionContext<TCollection> context)
: base(context)
{
}

public HasDistinctItemsAssertion(
AssertionContext<TCollection> context,
IEqualityComparer<TItem> comparer)
: base(context)
{
_comparer = comparer;
}

protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<TCollection> metadata)
{
if (metadata.Exception != null)
Expand All @@ -30,7 +40,7 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<TCollecti
}

var adapter = new EnumerableAdapter<TItem>(metadata.Value);
return Task.FromResult(CollectionChecks.CheckHasDistinctItems(adapter));
return Task.FromResult(CollectionChecks.CheckHasDistinctItems(adapter, _comparer));
}

protected override string GetExpectation() => "to have distinct items";
Expand Down
22 changes: 22 additions & 0 deletions TUnit.Assertions/Conditions/StringEqualsAssertion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace TUnit.Assertions.Conditions;
public class StringEqualsAssertion<TActual> : Assertion<TActual>
{
private readonly string? _expected;
private readonly IEqualityComparer<string>? _comparer;
private StringComparison _comparison = StringComparison.Ordinal;
private bool _trimming;
private bool _nullAndEmptyEquality;
Expand All @@ -36,6 +37,16 @@ public StringEqualsAssertion(
_comparison = comparison;
}

public StringEqualsAssertion(
AssertionContext<TActual> context,
string? expected,
IEqualityComparer<string> comparer)
: base(context)
{
_expected = expected;
_comparer = comparer;
}

/// <summary>
/// Makes the comparison case-insensitive.
/// </summary>
Expand Down Expand Up @@ -112,6 +123,17 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<TActual>
expectedValue = string.IsNullOrEmpty(expectedValue) ? null : expectedValue;
}

// When a custom comparer is provided, use it directly
if (_comparer != null)
{
if (_comparer.Equals(actualValue!, expectedValue!))
{
return AssertionResult._passedTask;
}

return Task.FromResult(AssertionResult.Failed($"found \"{value}\""));
}

if (string.Equals(actualValue, expectedValue, _comparison))
{
return AssertionResult._passedTask;
Expand Down
43 changes: 43 additions & 0 deletions TUnit.Assertions/Sources/CollectionAssertionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,21 @@ public CollectionContainsAssertion<TCollection, TItem> Contains(
return new CollectionContainsAssertion<TCollection, TItem>(Context, expected);
}

/// <summary>
/// Asserts that the collection contains the expected item using a custom equality comparer.
/// This instance method enables calling Contains with proper type inference and a custom comparer.
/// Example: await Assert.That(list).Contains("value", StringComparer.OrdinalIgnoreCase);
/// </summary>
public CollectionContainsAssertion<TCollection, TItem> Contains(
TItem expected,
IEqualityComparer<TItem> comparer,
[CallerArgumentExpression(nameof(expected))] string? expectedExpression = null,
[CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
{
Context.ExpressionBuilder.Append($".Contains({expectedExpression}, {comparerExpression})");
return new CollectionContainsAssertion<TCollection, TItem>(Context, expected, comparer);
}

/// <summary>
/// Asserts that the collection contains an item matching the predicate.
/// This instance method enables calling Contains with proper type inference.
Expand Down Expand Up @@ -284,6 +299,19 @@ public HasDistinctItemsAssertion<TCollection, TItem> HasDistinctItems()
return new HasDistinctItemsAssertion<TCollection, TItem>(Context);
}

/// <summary>
/// Asserts that the collection contains only distinct (unique) items using a custom equality comparer.
/// This instance method enables calling HasDistinctItems with proper type inference and a custom comparer.
/// Example: await Assert.That(list).HasDistinctItems(StringComparer.OrdinalIgnoreCase);
/// </summary>
public HasDistinctItemsAssertion<TCollection, TItem> HasDistinctItems(
IEqualityComparer<TItem> comparer,
[CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
{
Context.ExpressionBuilder.Append($".HasDistinctItems({comparerExpression})");
return new HasDistinctItemsAssertion<TCollection, TItem>(Context, comparer);
}

/// <summary>
/// Asserts that the collection does not contain the specified item.
/// This instance method enables calling DoesNotContain with proper type inference.
Expand All @@ -297,6 +325,21 @@ public CollectionDoesNotContainAssertion<TCollection, TItem> DoesNotContain(
return new CollectionDoesNotContainAssertion<TCollection, TItem>(Context, expected);
}

/// <summary>
/// Asserts that the collection does not contain the specified item using a custom equality comparer.
/// This instance method enables calling DoesNotContain with proper type inference and a custom comparer.
/// Example: await Assert.That(list).DoesNotContain("value", StringComparer.OrdinalIgnoreCase);
/// </summary>
public CollectionDoesNotContainAssertion<TCollection, TItem> DoesNotContain(
TItem expected,
IEqualityComparer<TItem> comparer,
[CallerArgumentExpression(nameof(expected))] string? expectedExpression = null,
[CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
{
Context.ExpressionBuilder.Append($".DoesNotContain({expectedExpression}, {comparerExpression})");
return new CollectionDoesNotContainAssertion<TCollection, TItem>(Context, expected, comparer);
}

/// <summary>
/// Asserts that the collection does not contain any item matching the predicate.
/// This instance method enables calling DoesNotContain with proper type inference.
Expand Down
26 changes: 26 additions & 0 deletions TUnit.Assertions/Sources/MemoryAssertionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,19 @@ public MemoryContainsAssertion<TMemory, TItem> Contains(
return new MemoryContainsAssertion<TMemory, TItem>(Context, CreateAdapter, expected);
}

/// <summary>
/// Asserts that the memory contains the expected item using a custom equality comparer.
/// </summary>
public MemoryContainsAssertion<TMemory, TItem> Contains(
TItem expected,
IEqualityComparer<TItem> comparer,
[CallerArgumentExpression(nameof(expected))] string? expectedExpression = null,
[CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
{
Context.ExpressionBuilder.Append($".Contains({expectedExpression}, {comparerExpression})");
return new MemoryContainsAssertion<TMemory, TItem>(Context, CreateAdapter, expected, comparer);
}

/// <summary>
/// Asserts that the memory does not contain the expected item.
/// </summary>
Expand All @@ -115,6 +128,19 @@ public MemoryDoesNotContainAssertion<TMemory, TItem> DoesNotContain(
return new MemoryDoesNotContainAssertion<TMemory, TItem>(Context, CreateAdapter, expected);
}

/// <summary>
/// Asserts that the memory does not contain the expected item using a custom equality comparer.
/// </summary>
public MemoryDoesNotContainAssertion<TMemory, TItem> DoesNotContain(
TItem expected,
IEqualityComparer<TItem> comparer,
[CallerArgumentExpression(nameof(expected))] string? expectedExpression = null,
[CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
{
Context.ExpressionBuilder.Append($".DoesNotContain({expectedExpression}, {comparerExpression})");
return new MemoryDoesNotContainAssertion<TMemory, TItem>(Context, CreateAdapter, expected, comparer);
}

/// <summary>
/// Gets the count for further numeric assertions.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,7 @@ namespace .Conditions
where TCollection : .<TItem>
{
public HasDistinctItemsAssertion(.<TCollection> context) { }
public HasDistinctItemsAssertion(.<TCollection> context, .<TItem> comparer) { }
protected override .<.> CheckAsync(.<TCollection> metadata) { }
protected override string GetExpectation() { }
}
Expand Down Expand Up @@ -1901,6 +1902,7 @@ namespace .Conditions
public class StringEqualsAssertion<TActual> : .<TActual>
{
public StringEqualsAssertion(.<TActual> context, string? expected) { }
public StringEqualsAssertion(.<TActual> context, string? expected, .<string> comparer) { }
public StringEqualsAssertion(.<TActual> context, string? expected, comparison) { }
protected override .<.> CheckAsync(.<TActual> metadata) { }
protected override string GetExpectation() { }
Expand Down Expand Up @@ -4819,6 +4821,7 @@ namespace .Extensions
public static class StringEqualsAssertionExtensions
{
public static .<TActual> IsEqualTo<TActual>(this .<TActual> source, string? expected, [.("expected")] string? expectedExpression = null) { }
public static .<TActual> IsEqualTo<TActual>(this .<TActual> source, string? expected, .<string> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
public static .<TActual> IsEqualTo<TActual>(this .<TActual> source, string? expected, comparison, [.("expected")] string? expectedExpression = null, [.("comparison")] string? comparisonExpression = null) { }
}
public static class StringIsEmptyAssertionExtensions
Expand Down Expand Up @@ -5663,20 +5666,23 @@ namespace .Sources
public .<TCollection, TItem> Any(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> Contains(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> Contains(TItem expected, [.("expected")] string? expression = null) { }
public .<TCollection, TItem> Contains(TItem expected, .<TItem> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
public .<TCollection, TItem> ContainsOnly(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> Count() { }
public .<TCollection, TItem> Count(<.<TItem>, .<TItem>?> itemAssertion, [.("itemAssertion")] string? expression = null) { }
[.(-1)]
public .<TCollection, TItem> Count(<.<int>, .<int>?> countAssertion, [.("countAssertion")] string? expression = null) { }
public .<TCollection, TItem> DoesNotContain(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> DoesNotContain(TItem expected, [.("expected")] string? expression = null) { }
public .<TCollection, TItem> DoesNotContain(TItem expected, .<TItem> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
protected override string GetExpectation() { }
[("Use Count() instead, which provides all numeric assertion methods. Example: Asser" +
"(list).Count().IsGreaterThan(5)")]
public ..CountWrapper<TCollection, TItem> HasCount() { }
[("Use Count().IsEqualTo(expectedCount) instead.")]
public .<TCollection, TItem> HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { }
public .<TCollection, TItem> HasDistinctItems() { }
public .<TCollection, TItem> HasDistinctItems(.<TItem> comparer, [.("comparer")] string? comparerExpression = null) { }
public .<TCollection, TItem> HasSingleItem() { }
public .<TCollection, TItem> HasSingleItem(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TTarget, TCollection> IsAssignableTo<TTarget>() { }
Expand Down Expand Up @@ -5786,9 +5792,11 @@ namespace .Sources
public .<TMemory, TItem> All(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TMemory, TItem> Any(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TMemory, TItem> Contains(TItem expected, [.("expected")] string? expression = null) { }
public .<TMemory, TItem> Contains(TItem expected, .<TItem> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
public .<TMemory, TItem> Count() { }
protected abstract .<TItem> CreateAdapter(TMemory value);
public .<TMemory, TItem> DoesNotContain(TItem expected, [.("expected")] string? expression = null) { }
public .<TMemory, TItem> DoesNotContain(TItem expected, .<TItem> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
protected override string GetExpectation() { }
public .<TMemory, TItem> HasDistinctItems() { }
public .<TMemory, TItem> HasSingleItem() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,7 @@ namespace .Conditions
where TCollection : .<TItem>
{
public HasDistinctItemsAssertion(.<TCollection> context) { }
public HasDistinctItemsAssertion(.<TCollection> context, .<TItem> comparer) { }
protected override .<.> CheckAsync(.<TCollection> metadata) { }
protected override string GetExpectation() { }
}
Expand Down Expand Up @@ -1884,6 +1885,7 @@ namespace .Conditions
public class StringEqualsAssertion<TActual> : .<TActual>
{
public StringEqualsAssertion(.<TActual> context, string? expected) { }
public StringEqualsAssertion(.<TActual> context, string? expected, .<string> comparer) { }
public StringEqualsAssertion(.<TActual> context, string? expected, comparison) { }
protected override .<.> CheckAsync(.<TActual> metadata) { }
protected override string GetExpectation() { }
Expand Down Expand Up @@ -4769,6 +4771,7 @@ namespace .Extensions
public static class StringEqualsAssertionExtensions
{
public static .<TActual> IsEqualTo<TActual>(this .<TActual> source, string? expected, [.("expected")] string? expectedExpression = null) { }
public static .<TActual> IsEqualTo<TActual>(this .<TActual> source, string? expected, .<string> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
public static .<TActual> IsEqualTo<TActual>(this .<TActual> source, string? expected, comparison, [.("expected")] string? expectedExpression = null, [.("comparison")] string? comparisonExpression = null) { }
}
public static class StringIsEmptyAssertionExtensions
Expand Down Expand Up @@ -5611,19 +5614,22 @@ namespace .Sources
public .<TCollection, TItem> Any(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> Contains(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> Contains(TItem expected, [.("expected")] string? expression = null) { }
public .<TCollection, TItem> Contains(TItem expected, .<TItem> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
public .<TCollection, TItem> ContainsOnly(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> Count() { }
public .<TCollection, TItem> Count(<.<TItem>, .<TItem>?> itemAssertion, [.("itemAssertion")] string? expression = null) { }
public .<TCollection, TItem> Count(<.<int>, .<int>?> countAssertion, [.("countAssertion")] string? expression = null) { }
public .<TCollection, TItem> DoesNotContain(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TCollection, TItem> DoesNotContain(TItem expected, [.("expected")] string? expression = null) { }
public .<TCollection, TItem> DoesNotContain(TItem expected, .<TItem> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
protected override string GetExpectation() { }
[("Use Count() instead, which provides all numeric assertion methods. Example: Asser" +
"(list).Count().IsGreaterThan(5)")]
public ..CountWrapper<TCollection, TItem> HasCount() { }
[("Use Count().IsEqualTo(expectedCount) instead.")]
public .<TCollection, TItem> HasCount(int expectedCount, [.("expectedCount")] string? expression = null) { }
public .<TCollection, TItem> HasDistinctItems() { }
public .<TCollection, TItem> HasDistinctItems(.<TItem> comparer, [.("comparer")] string? comparerExpression = null) { }
public .<TCollection, TItem> HasSingleItem() { }
public .<TCollection, TItem> HasSingleItem(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TTarget, TCollection> IsAssignableTo<TTarget>() { }
Expand Down Expand Up @@ -5733,9 +5739,11 @@ namespace .Sources
public .<TMemory, TItem> All(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TMemory, TItem> Any(<TItem, bool> predicate, [.("predicate")] string? expression = null) { }
public .<TMemory, TItem> Contains(TItem expected, [.("expected")] string? expression = null) { }
public .<TMemory, TItem> Contains(TItem expected, .<TItem> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
public .<TMemory, TItem> Count() { }
protected abstract .<TItem> CreateAdapter(TMemory value);
public .<TMemory, TItem> DoesNotContain(TItem expected, [.("expected")] string? expression = null) { }
public .<TMemory, TItem> DoesNotContain(TItem expected, .<TItem> comparer, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null) { }
protected override string GetExpectation() { }
public .<TMemory, TItem> HasDistinctItems() { }
public .<TMemory, TItem> HasSingleItem() { }
Expand Down
Loading
Loading