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 @@ -36,7 +36,7 @@ sut.SetupMock.Property.TotalDispensed
.Returns(10).For(1)
.Returns(20).For(2)
.Returns(30).For(3);
// Reads: 10, 20, 20, 30, 30, 30, 0, 0, 0, 0
// Reads: 10, 20, 20, 30, 30, 30, 10, 20, 20, 30, 30, 30
```

### Repeat `Forever`
Expand Down
41 changes: 27 additions & 14 deletions Source/Mockolate/Setup/Callback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,7 @@ public class Callback
/// A <see cref="Callback" /> is active, until it has reached the <see cref="Only" /> limit, if specified.
/// </remarks>
protected bool IsActive(int matchingCount)
{
if (_forTimes is not null)
{
matchingCount -= _forTimes.Value;
}

return _onlyTimes == 0 || matchingCount < _onlyTimes;
}
=> _onlyTimes == 0 || matchingCount < _onlyTimes * (_forTimes ?? 1);

/// <summary>
/// Limits the callback to only execute for property accesses where the predicate returns <see langword="true" />.
Expand Down Expand Up @@ -105,6 +98,7 @@ protected bool CheckMatching(int matchingCount)
/// </summary>
public class Callback<TDelegate>(TDelegate @delegate) : Callback where TDelegate : Delegate
{
private int _forIterationCount;
private int _invocationCount;
private int _matchingCount;

Expand All @@ -116,7 +110,7 @@ public bool Invoke(bool wasInvoked, ref int index, Action<int, TDelegate> callba
{
if (IsActive(_matchingCount) && CheckInvocations(_invocationCount))
{
if (CheckMatching(_matchingCount))
if (CheckMatching(_forIterationCount))
{
_invocationCount++;

Expand All @@ -127,14 +121,20 @@ public bool Invoke(bool wasInvoked, ref int index, Action<int, TDelegate> callba
Interlocked.Increment(ref index);
}

_forIterationCount++;
_matchingCount++;
callback(_invocationCount - 1, @delegate);
}
else if (!wasInvoked)
{
if (!HasForSpecified || !CheckMatching(_matchingCount + 1))
if (!HasForSpecified || !CheckMatching(_forIterationCount + 1))
{
Interlocked.Increment(ref index);
_forIterationCount = 0;
}
else
{
_forIterationCount++;
}

_matchingCount++;
Expand All @@ -144,6 +144,7 @@ public bool Invoke(bool wasInvoked, ref int index, Action<int, TDelegate> callba
return !RunInParallel;
}

_forIterationCount++;
_matchingCount++;
}

Expand All @@ -159,11 +160,16 @@ public bool Invoke(ref int index, Action<int, TDelegate> callback)
{
if (IsActive(_matchingCount) && CheckInvocations(_invocationCount))
{
if (CheckMatching(_matchingCount))
if (CheckMatching(_forIterationCount))
{
if (!HasForSpecified || !CheckMatching(_matchingCount + 1))
if (!HasForSpecified || !CheckMatching(_forIterationCount + 1))
{
Interlocked.Increment(ref index);
_forIterationCount = 0;
}
else
{
_forIterationCount++;
}

_invocationCount++;
Expand All @@ -172,6 +178,7 @@ public bool Invoke(ref int index, Action<int, TDelegate> callback)
return true;
}

_forIterationCount++;
_matchingCount++;
}

Expand All @@ -188,11 +195,16 @@ public bool Invoke<TReturn>(ref int index, Func<int, TDelegate, TReturn> callbac
{
if (IsActive(_matchingCount) && CheckInvocations(_invocationCount))
{
if (CheckMatching(_matchingCount))
if (CheckMatching(_forIterationCount))
{
if (!HasForSpecified || !CheckMatching(_matchingCount + 1))
if (!HasForSpecified || !CheckMatching(_forIterationCount + 1))
{
Interlocked.Increment(ref index);
_forIterationCount = 0;
}
else
{
_forIterationCount++;
}

_invocationCount++;
Expand All @@ -201,6 +213,7 @@ public bool Invoke<TReturn>(ref int index, Func<int, TDelegate, TReturn> callbac
return true;
}

_forIterationCount++;
_matchingCount++;
}

Expand Down
96 changes: 17 additions & 79 deletions Tests/Mockolate.Internal.Tests/CallbackTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public async Task ShouldIncludeIndexWhenMatching()
bool wasInvoked = false;
List<int> values = [];
Callback<Action> sut = new(() => { });
sut.For(2);
sut.Only(2);
sut.When(v => v > 1);

int index = 0;
Expand All @@ -26,16 +26,16 @@ public async Task ShouldIncludeIndexWhenMatching()
}

[Theory]
[InlineData(2, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1)]
[InlineData(2, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1)]
[InlineData(2, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2)]
[InlineData(2, 1, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2)]
public async Task ShouldIncrementIndexOnceWhenCallbackIsExhausted(
int forValue, int whenMinimum, params int[] expectResult)
int only, int when, params int[] expectResult)
{
bool wasInvoked = false;
List<int> indexValues = [];
Callback<Action> sut = new(() => { });
sut.For(forValue);
sut.When(v => v > whenMinimum);
sut.Only(only);
sut.When(v => v > when);

int index = 0;
for (int iteration = 1; iteration <= 10; iteration++)
Expand All @@ -51,7 +51,7 @@ public async Task ShouldIncrementIndexOnceWhenCallbackIsExhausted(
public async Task WhenCheck_ShouldOnlyBeCalledWhenOnlyLimitIsNotReached()
{
bool wasInvoked = false;
int[] expectResult = [0, 1, 2, 3, 4, 5,];
int[] expectResult = [0, 1, 2, 3, 4, 5, 6, 7,];
List<int> invocationChecks = [];
int invocationCount = 0;
Callback<Action> sut = new(() => { invocationCount++; });
Expand All @@ -70,7 +70,7 @@ public async Task WhenCheck_ShouldOnlyBeCalledWhenOnlyLimitIsNotReached()
}

await That(invocationChecks).IsEqualTo(expectResult);
await That(invocationCount).IsEqualTo(2);
await That(invocationCount).IsEqualTo(8);
}

[Theory]
Expand All @@ -84,7 +84,7 @@ public async Task WithForAndWhen_ShouldMatchInExpectedIterations(
{
bool wasInvoked = false;
Callback<Action> sut = new(() => { });
sut.For(2);
sut.Only(2);
sut.When(v => v > 1);

int index = 0;
Expand All @@ -105,7 +105,7 @@ public async Task ShouldIncludeIndexWhenMatching()
{
List<int> values = [];
Callback<Action> sut = new(() => { });
sut.For(2);
sut.Only(2);
sut.When(v => v > 1);

int index = 0;
Expand All @@ -117,39 +117,10 @@ public async Task ShouldIncludeIndexWhenMatching()
await That(values).IsEqualTo([2, 3,]);
}

[Theory]
[InlineData(2, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9)]
[InlineData(2, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9)]
[InlineData(3, 1, 1, 2, 2, 2, 3, 4, 5, 6, 7, 8)]
[InlineData(5, 4, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6)]
[InlineData(6, 4, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5)]
[InlineData(7, 4, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5)]
[InlineData(3, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)]
public async Task ShouldIncrementIndexWhenForIsNotActive(
int forValue, int whenMinimum, params int[] expectResult)
{
List<int> indexValues = [];
int invocationCount = 0;
int expectedInvocationCount = Math.Max(0, Math.Min(forValue, 9 - whenMinimum));
Callback<Action> sut = new(() => { invocationCount++; });
sut.For(forValue);
sut.When(v => v > whenMinimum);

int index = 0;
for (int iteration = 1; iteration <= 10; iteration++)
{
sut.Invoke(ref index, (_, c) => c());
indexValues.Add(index);
}

await That(indexValues).IsEqualTo(expectResult);
await That(invocationCount).IsEqualTo(expectedInvocationCount);
}

[Fact]
public async Task WhenCheck_ShouldOnlyBeCalledWhenOnlyLimitIsNotReached()
{
int[] expectResult = [0, 1, 2, 3, 4, 5,];
int[] expectResult = [0, 1, 2, 3, 4, 5, 6, 7,];
List<int> invocationChecks = [];
int invocationCount = 0;
Callback<Action> sut = new(() => { invocationCount++; });
Expand All @@ -168,7 +139,7 @@ public async Task WhenCheck_ShouldOnlyBeCalledWhenOnlyLimitIsNotReached()
}

await That(invocationChecks).IsEqualTo(expectResult);
await That(invocationCount).IsEqualTo(2);
await That(invocationCount).IsEqualTo(8);
}

[Theory]
Expand All @@ -181,7 +152,7 @@ public async Task WithForAndWhen_ShouldMatchInExpectedIterations(
int iterations, bool expectResult)
{
Callback<Action> sut = new(() => { });
sut.For(2);
sut.Only(2);
sut.When(v => v > 1);

int index = 0;
Expand All @@ -202,7 +173,7 @@ public async Task ShouldIncludeIndexWhenMatching()
{
List<int> values = [];
Callback<Action> sut = new(() => { });
sut.For(2);
sut.Only(2);
sut.When(v => v > 1);

int index = 0;
Expand All @@ -218,43 +189,10 @@ public async Task ShouldIncludeIndexWhenMatching()
await That(values).IsEqualTo([2, 3,]);
}

[Theory]
[InlineData(2, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9)]
[InlineData(2, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9)]
[InlineData(3, 1, 1, 2, 2, 2, 3, 4, 5, 6, 7, 8)]
[InlineData(5, 4, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6)]
[InlineData(6, 4, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5)]
[InlineData(7, 4, 1, 2, 3, 4, 5, 5, 5, 5, 5, 5)]
[InlineData(3, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)]
public async Task ShouldIncrementIndexWhenForIsNotActive(
int forValue, int whenMinimum, params int[] expectResult)
{
List<int> indexValues = [];
int invocationCount = 0;
int expectedInvocationCount = Math.Max(0, Math.Min(forValue, 9 - whenMinimum));
Callback<Action> sut = new(() => { invocationCount++; });
sut.For(forValue);
sut.When(v => v > whenMinimum);

int index = 0;
for (int iteration = 1; iteration <= 10; iteration++)
{
sut.Invoke(ref index, (_, c) =>
{
c();
return "foo";
}, out string? _);
indexValues.Add(index);
}

await That(indexValues).IsEqualTo(expectResult);
await That(invocationCount).IsEqualTo(expectedInvocationCount);
}

[Fact]
public async Task WhenCheck_ShouldOnlyBeCalledWhenOnlyLimitIsNotReached()
{
int[] expectResult = [0, 1, 2, 3, 4, 5,];
int[] expectResult = [0, 1, 2, 3, 4, 5, 6, 7,];
List<int> invocationChecks = [];
int invocationCount = 0;
Callback<Action> sut = new(() => { invocationCount++; });
Expand All @@ -277,7 +215,7 @@ public async Task WhenCheck_ShouldOnlyBeCalledWhenOnlyLimitIsNotReached()
}

await That(invocationChecks).IsEqualTo(expectResult);
await That(invocationCount).IsEqualTo(2);
await That(invocationCount).IsEqualTo(8);
}

[Theory]
Expand All @@ -290,7 +228,7 @@ public async Task WithForAndWhen_ShouldMatchInExpectedIterations(
int iterations, bool expectResult)
{
Callback<Action> sut = new(() => { });
sut.For(2);
sut.Only(2);
sut.When(v => v > 1);

int index = 0;
Expand Down
Loading
Loading