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
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ public partial class Elements<TItem>
}
}

public partial class Elements
{
/// <summary>
/// …comply with the <paramref name="expectations" />.
/// </summary>
public ObjectEqualityResult<IAsyncEnumerable<string?>, IThat<IAsyncEnumerable<string?>?>, string?>
ComplyWith(Action<IThatSubject<string?>> expectations)
{
ObjectEqualityOptions<string?> options = new();
return new ObjectEqualityResult<IAsyncEnumerable<string?>, IThat<IAsyncEnumerable<string?>?>, string?>(
_subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars)
=> new ComplyWithConstraint<string?>(expectationBuilder, it, grammars, _quantifier, expectations)),
_subject,
options);
}
}

private sealed class ComplyWithConstraint<TItem>
: ConstraintResult.WithValue<IAsyncEnumerable<TItem>?>,
IAsyncContextConstraint<IAsyncEnumerable<TItem>?>
Expand Down
110 changes: 110 additions & 0 deletions Source/aweXpect/That/Collections/ThatEnumerable.Elements.ComplyWith.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,116 @@ protected override void AppendNegatedResult(StringBuilder stringBuilder, string?
}
}

public partial class Elements
{
/// <summary>
/// …comply with the <paramref name="expectations" />.
/// </summary>
public ObjectEqualityResult<IEnumerable<string?>, IThat<IEnumerable<string?>?>, string?>
ComplyWith(Action<IThatSubject<string?>> expectations)
{
ObjectEqualityOptions<string?> options = new();
return new ObjectEqualityResult<IEnumerable<string?>, IThat<IEnumerable<string?>?>, string?>(
_subject.Get().ExpectationBuilder.AddConstraint((expectationBuilder, it, grammars)
=> new ComplyWithConstraint(expectationBuilder, it, grammars, _quantifier, expectations)),
_subject,
options);
}
Comment thread
vbreuss marked this conversation as resolved.

private sealed class ComplyWithConstraint
: ConstraintResult.WithNotNullValue<IEnumerable<string?>?>,
IAsyncContextConstraint<IEnumerable<string?>?>
{
private readonly ManualExpectationBuilder<string?> _itemExpectationBuilder;
private readonly EnumerableQuantifier _quantifier;
private int _matchingCount;
private int _notMatchingCount;
private int? _totalCount;

public ComplyWithConstraint(ExpectationBuilder expectationBuilder, string it, ExpectationGrammars grammars,
EnumerableQuantifier quantifier,
Action<IThatSubject<string?>> expectations)
: base(it, grammars)
{
_quantifier = quantifier;
_itemExpectationBuilder = new ManualExpectationBuilder<string?>(expectationBuilder, grammars);
expectations.Invoke(new ThatSubject<string?>(_itemExpectationBuilder));
}

public async Task<ConstraintResult> IsMetBy(
IEnumerable<string?>? actual,
IEvaluationContext context,
CancellationToken cancellationToken)
{
Actual = actual;
if (actual is null)
{
Outcome = Outcome.Failure;
return this;
}

IEnumerable<string?> materialized = context.UseMaterializedEnumerable<string?, IEnumerable<string?>>(actual);
bool cancelEarly = actual is not ICollection<string?>;
_matchingCount = 0;
_notMatchingCount = 0;

foreach (string? item in materialized)
{
ConstraintResult isMatch = await _itemExpectationBuilder.IsMetBy(item, context, cancellationToken);
if (isMatch.Outcome == Outcome.Success)
{
_matchingCount++;
}
else
{
_notMatchingCount++;
}

if (cancelEarly && _quantifier.IsDeterminable(_matchingCount, _notMatchingCount))
{
Outcome = _quantifier.GetOutcome(_matchingCount, _notMatchingCount, _totalCount);
return this;
}

if (cancellationToken.IsCancellationRequested)
{
Outcome = Outcome.Undecided;
return this;
}
}

_totalCount = _matchingCount + _notMatchingCount;
Outcome = _quantifier.GetOutcome(_matchingCount, _notMatchingCount, _totalCount);
return this;
}

protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null)
{
_itemExpectationBuilder.AppendExpectation(stringBuilder, indentation);
stringBuilder.Append(For);
stringBuilder.Append(_quantifier);
stringBuilder.Append(' ');
stringBuilder.Append(_quantifier.GetItemString());
}

protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null)
=> _quantifier.AppendResult(stringBuilder, Grammars, _matchingCount, _notMatchingCount, _totalCount);

protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null)
{
stringBuilder.Append(_quantifier);
stringBuilder.Append(For);
_itemExpectationBuilder.AppendExpectation(stringBuilder, indentation);
stringBuilder.Append(' ');
stringBuilder.Append(_quantifier.GetItemString());
}

protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null)
=> _quantifier.AppendResult(stringBuilder, Grammars, _matchingCount, _notMatchingCount,
_totalCount);
}
}

public partial class ElementsForEnumerable<TEnumerable>
{
/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions Tests/aweXpect.Api.Tests/Expected/aweXpect_net10.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ namespace aweXpect
public static aweXpect.Results.ObjectEqualityResult<System.Collections.Generic.IAsyncEnumerable<TItem>, aweXpect.Core.IThat<System.Collections.Generic.IAsyncEnumerable<TItem>?>, TItem> StartsWith<TItem>(this aweXpect.Core.IThat<System.Collections.Generic.IAsyncEnumerable<TItem>?> source, System.Collections.Generic.IEnumerable<TItem> expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = "") { }
public class Elements : aweXpect.ThatAsyncEnumerable.IElements
{
public aweXpect.Results.ObjectEqualityResult<System.Collections.Generic.IAsyncEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IAsyncEnumerable<string?>?>, string?> ComplyWith(System.Action<aweXpect.Core.IThatSubject<string?>> expectations) { }
public aweXpect.Results.AndOrResult<System.Collections.Generic.IAsyncEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IAsyncEnumerable<string?>?>> Satisfy(System.Func<string?, bool> predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { }
}
public class Elements<TItem> : aweXpect.ThatAsyncEnumerable.IElements<TItem>
Expand Down Expand Up @@ -631,6 +632,7 @@ namespace aweXpect
public static aweXpect.Results.ObjectEqualityResult<System.Collections.Immutable.ImmutableArray<TItem>, aweXpect.Core.IThat<System.Collections.Immutable.ImmutableArray<TItem>>, TItem> StartsWith<TItem>(this aweXpect.Core.IThat<System.Collections.Immutable.ImmutableArray<TItem>> source, System.Collections.Generic.IEnumerable<TItem> expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = "") { }
public class Elements : aweXpect.ThatEnumerable.IElements
{
public aweXpect.Results.ObjectEqualityResult<System.Collections.Generic.IEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IEnumerable<string?>?>, string?> ComplyWith(System.Action<aweXpect.Core.IThatSubject<string?>> expectations) { }
public aweXpect.Results.AndOrResult<System.Collections.Generic.IEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IEnumerable<string?>?>> Satisfy(System.Func<string?, bool> predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { }
}
public class Elements<TItem> : aweXpect.ThatEnumerable.IElements<TItem>
Expand Down
2 changes: 2 additions & 0 deletions Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ namespace aweXpect
public static aweXpect.Results.ObjectEqualityResult<System.Collections.Generic.IAsyncEnumerable<TItem>, aweXpect.Core.IThat<System.Collections.Generic.IAsyncEnumerable<TItem>?>, TItem> StartsWith<TItem>(this aweXpect.Core.IThat<System.Collections.Generic.IAsyncEnumerable<TItem>?> source, System.Collections.Generic.IEnumerable<TItem> expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = "") { }
public class Elements : aweXpect.ThatAsyncEnumerable.IElements
{
public aweXpect.Results.ObjectEqualityResult<System.Collections.Generic.IAsyncEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IAsyncEnumerable<string?>?>, string?> ComplyWith(System.Action<aweXpect.Core.IThatSubject<string?>> expectations) { }
public aweXpect.Results.AndOrResult<System.Collections.Generic.IAsyncEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IAsyncEnumerable<string?>?>> Satisfy(System.Func<string?, bool> predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { }
}
public class Elements<TItem> : aweXpect.ThatAsyncEnumerable.IElements<TItem>
Expand Down Expand Up @@ -631,6 +632,7 @@ namespace aweXpect
public static aweXpect.Results.ObjectEqualityResult<System.Collections.Immutable.ImmutableArray<TItem>, aweXpect.Core.IThat<System.Collections.Immutable.ImmutableArray<TItem>>, TItem> StartsWith<TItem>(this aweXpect.Core.IThat<System.Collections.Immutable.ImmutableArray<TItem>> source, System.Collections.Generic.IEnumerable<TItem> expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = "") { }
public class Elements : aweXpect.ThatEnumerable.IElements
{
public aweXpect.Results.ObjectEqualityResult<System.Collections.Generic.IEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IEnumerable<string?>?>, string?> ComplyWith(System.Action<aweXpect.Core.IThatSubject<string?>> expectations) { }
public aweXpect.Results.AndOrResult<System.Collections.Generic.IEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IEnumerable<string?>?>> Satisfy(System.Func<string?, bool> predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { }
}
public class Elements<TItem> : aweXpect.ThatEnumerable.IElements<TItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ namespace aweXpect
public static aweXpect.Results.ObjectEqualityResult<System.Collections.Generic.IEnumerable<TItem>, aweXpect.Core.IThat<System.Collections.Generic.IEnumerable<TItem>?>, TItem> StartsWith<TItem>(this aweXpect.Core.IThat<System.Collections.Generic.IEnumerable<TItem>?> source, System.Collections.Generic.IEnumerable<TItem> expected, [System.Runtime.CompilerServices.CallerArgumentExpression("expected")] string doNotPopulateThisValue = "") { }
public class Elements : aweXpect.ThatEnumerable.IElements
{
public aweXpect.Results.ObjectEqualityResult<System.Collections.Generic.IEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IEnumerable<string?>?>, string?> ComplyWith(System.Action<aweXpect.Core.IThatSubject<string?>> expectations) { }
public aweXpect.Results.AndOrResult<System.Collections.Generic.IEnumerable<string?>, aweXpect.Core.IThat<System.Collections.Generic.IEnumerable<string?>?>> Satisfy(System.Func<string?, bool> predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { }
}
public class Elements<TItem> : aweXpect.ThatEnumerable.IElements<TItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ but not all were
[Fact]
public async Task WhenEnumerableIsEmpty_ShouldSucceed()
{
IAsyncEnumerable<int> subject = ToAsyncEnumerable((int[]) []);
IAsyncEnumerable<int> subject = ToAsyncEnumerable((int[])[]);

async Task Act()
=> await That(subject).All().ComplyWith(x => x.IsEqualTo(0));
Expand Down Expand Up @@ -115,6 +115,72 @@ but it was <null>
}
}

public sealed class StringTests
{
[Fact]
public async Task WhenAllItemsMatchExpectation_ShouldSucceed()
{
IAsyncEnumerable<string?> subject = ToAsyncEnumerable<string?>("apple", "ant", "avocado");

async Task Act()
=> await That(subject).All().ComplyWith(it => it.StartsWith("a"));

await That(Act).DoesNotThrow();
}

[Fact]
public async Task WhenExactlyOneItemMatchesExpectation_ShouldSucceed()
{
IAsyncEnumerable<string?> subject = ToAsyncEnumerable<string?>("apple", "banana", "cherry");

async Task Act()
=> await That(subject).Exactly(1).ComplyWith(it => it.StartsWith("b"));

await That(Act).DoesNotThrow();
}

[Fact]
public async Task WhenNotAllItemsMatch_ShouldFail()
{
IAsyncEnumerable<string?> subject = ToAsyncEnumerable<string?>("apple", "banana", "avocado");

async Task Act()
=> await That(subject).All().ComplyWith(it => it.StartsWith("a"));

await That(Act).Throws<XunitException>()
.WithMessage("""
Expected that subject
starts with "a" for all items,
but not all were

Actual:
apple

Actual:
banana

Expected:
a
""");
}

[Fact]
public async Task WhenSubjectIsNull_ShouldFail()
{
IAsyncEnumerable<string?>? subject = null;

async Task Act()
=> await That(subject).All().ComplyWith(it => it.StartsWith("a"));

await That(Act).Throws<XunitException>()
.WithMessage("""
Expected that subject
starts with "a" for all items,
but it was <null>
""");
}
Comment thread
vbreuss marked this conversation as resolved.
}

public sealed class NegatedTests
{
[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Collections.Generic;

namespace aweXpect.Tests;

public sealed partial class ThatEnumerable
{
public sealed partial class All
{
public sealed partial class ComplyWith
{
public sealed class StringTests
{
[Fact]
public async Task WhenAllItemsMatchExpectation_ShouldSucceed()
{
IEnumerable<string?> subject = ["apple", "ant", "avocado",];

async Task Act()
=> await That(subject).All().ComplyWith(it => it.StartsWith("a"));

await That(Act).DoesNotThrow();
}

[Fact]
public async Task WhenExactlyOneItemMatchesExpectation_ShouldSucceed()
{
IEnumerable<string?> subject = ["apple", "banana", "cherry",];

async Task Act()
=> await That(subject).Exactly(1).ComplyWith(it => it.StartsWith("b"));

await That(Act).DoesNotThrow();
}

[Fact]
public async Task WhenNotAllItemsMatch_ShouldFail()
{
IEnumerable<string?> subject = ["apple", "banana", "avocado",];

async Task Act()
=> await That(subject).All().ComplyWith(it => it.StartsWith("a"));

await That(Act).Throws<XunitException>()
.WithMessage("""
Expected that subject
starts with "a" for all items,
but only 2 of 3 were
""").AsPrefix();
}

[Fact]
public async Task WhenSubjectIsNull_ShouldFail()
{
IEnumerable<string?>? subject = null;

async Task Act()
=> await That(subject).All().ComplyWith(it => it.StartsWith("a"));

await That(Act).Throws<XunitException>()
.WithMessage("""
Expected that subject
starts with "a" for all items,
but it was <null>
""");
}
Comment thread
vbreuss marked this conversation as resolved.
}
}
}
}
Loading