Skip to content

Commit 99c27e0

Browse files
author
Jos Hickson
committed
Try to make time-based tests more robust without reducing their validity.
1 parent 1ea9a82 commit 99c27e0

2 files changed

Lines changed: 75 additions & 87 deletions

File tree

Winton.Extensions.Threading.Actor.Tests.Unit/ActorTests.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -924,25 +924,30 @@ private void ThrowIfWaitTimesOut(Task task)
924924

925925
private static IActorTaskFactory SetUpTaskFactory()
926926
{
927-
var taskCreator = new ActorTaskFactory();
927+
var realTaskFactory = new ActorTaskFactory();
928928
var taskFactory = Mock.Of<IActorTaskFactory>();
929929

930930
Mock.Get(taskFactory)
931931
.Setup(x => x.Create(It.IsAny<Action<object>>(), It.IsAny<CancellationToken>(), It.IsAny<TaskCreationOptions>(), It.IsAny<object>()))
932-
.Returns<Action<object>, CancellationToken, TaskCreationOptions, object>((action, cancellationToken, taskCreationOptions, state) => taskCreator.Create(action, cancellationToken, taskCreationOptions, state));
932+
.Returns<Action<object>, CancellationToken, TaskCreationOptions, object>((action, cancellationToken, taskCreationOptions, state) => realTaskFactory.Create(action, cancellationToken, taskCreationOptions, state));
933933
Mock.Get(taskFactory)
934934
.Setup(x => x.Create(It.IsAny<Func<object, int>>(), It.IsAny<CancellationToken>(), It.IsAny<TaskCreationOptions>(), It.IsAny<object>()))
935-
.Returns<Func<object, int>, CancellationToken, TaskCreationOptions, object>((function, cancellationToken, taskCreationOptions, state) => taskCreator.Create(function, cancellationToken, taskCreationOptions, state));
935+
.Returns<Func<object, int>, CancellationToken, TaskCreationOptions, object>((function, cancellationToken, taskCreationOptions, state) => realTaskFactory.Create(function, cancellationToken, taskCreationOptions, state));
936936
Mock.Get(taskFactory)
937937
.Setup(x => x.Create(It.IsAny<Func<object, Task>>(), It.IsAny<CancellationToken>(), It.IsAny<TaskCreationOptions>(), It.IsAny<object>()))
938-
.Returns<Func<object, Task>, CancellationToken, TaskCreationOptions, object>((function, cancellationToken, taskCreationOptions, state) => taskCreator.Create(function, cancellationToken, taskCreationOptions, state));
938+
.Returns<Func<object, Task>, CancellationToken, TaskCreationOptions, object>((function, cancellationToken, taskCreationOptions, state) => realTaskFactory.Create(function, cancellationToken, taskCreationOptions, state));
939939
Mock.Get(taskFactory)
940940
.Setup(x => x.Create(It.IsAny<Func<object, Task<string>>>(), It.IsAny<CancellationToken>(), It.IsAny<TaskCreationOptions>(), It.IsAny<object>()))
941-
.Returns<Func<object, Task<string>>, CancellationToken, TaskCreationOptions, object>((function, cancellationToken, taskCreationOptions, state) => taskCreator.Create(function, cancellationToken, taskCreationOptions, state));
941+
.Returns<Func<object, Task<string>>, CancellationToken, TaskCreationOptions, object>((function, cancellationToken, taskCreationOptions, state) => realTaskFactory.Create(function, cancellationToken, taskCreationOptions, state));
942942
Mock.Get(taskFactory)
943-
.Setup(x => x.FromCompleted()).Returns(taskCreator.FromCompleted);
943+
.Setup(x => x.FromCompleted())
944+
.Returns(realTaskFactory.FromCompleted);
944945
Mock.Get(taskFactory)
945-
.Setup(x => x.FromException(It.IsAny<Exception>())).Returns<Exception>(taskCreator.FromException);
946+
.Setup(x => x.FromException(It.IsAny<Exception>()))
947+
.Returns<Exception>(realTaskFactory.FromException);
948+
Mock.Get(taskFactory)
949+
.Setup(x => x.CreateDelay(It.IsAny<TimeSpan>(), It.IsAny<CancellationToken>()))
950+
.Returns<TimeSpan, CancellationToken>(Task.Delay);
946951

947952
return taskFactory;
948953
}

Winton.Extensions.Threading.Actor.Tests.Unit/Internal/ActorWorkSchedulerTests.cs

Lines changed: 63 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,30 @@
1414

1515
namespace Winton.Extensions.Threading.Actor.Tests.Unit.Internal
1616
{
17-
public sealed class ActorWorkSchedulerTests
17+
public sealed class ActorWorkSchedulerTests : IDisposable
1818
{
1919
public enum WorkType
2020
{
2121
Sync,
2222
Async
2323
}
2424

25-
private IActor _actor;
26-
private IActorWorkScheduler _scheduler;
25+
private readonly IActor _actor;
26+
private readonly IActorWorkScheduler _scheduler;
27+
private readonly IActorTaskFactory _actorTaskFactory;
2728

2829
public ActorWorkSchedulerTests()
2930
{
30-
var actorTaskFactory = new ActorTaskFactory();
31-
_actor = new Actor(actorTaskFactory);
31+
_actorTaskFactory = SetUpTaskFactory();
32+
_actor = new Actor(_actorTaskFactory);
3233
_actor.Start();
33-
_scheduler = new ActorWorkScheduler(_actor, actorTaskFactory);
34+
_scheduler = new ActorWorkScheduler(_actor, _actorTaskFactory);
35+
}
36+
37+
public void Dispose()
38+
{
39+
_scheduler.CancelCurrent();
40+
_actor.Stop();
3441
}
3542

3643
[Theory]
@@ -51,9 +58,6 @@ public void ShouldBeAbleToScheduleWorkToRepeatAtAFixedInterval(WorkType workType
5158
}
5259
};
5360

54-
// First scheduled call to add should not be immediate so mark start ...
55-
adder();
56-
5761
switch (workType)
5862
{
5963
case WorkType.Sync:
@@ -86,11 +90,10 @@ public void ShouldBeAbleToScheduleWorkToRepeatAtAFixedInterval(WorkType workType
8690
[InlineData(WorkType.Sync)]
8791
public void ShouldBeAbleToScheduleWorkToStartImmediatelyBeforeRepeatingAtIntervals(WorkType workType)
8892
{
89-
var expectedInterval = TimeSpan.FromMilliseconds(100);
93+
var expectedInterval = TimeSpan.FromMilliseconds(5000);
9094
var scheduleTime = DateTime.UtcNow;
9195
DateTime? firstWork = null;
9296

93-
9497
switch (workType)
9598
{
9699
case WorkType.Sync:
@@ -119,39 +122,29 @@ public void ShouldBeAbleToScheduleWorkToStartImmediatelyBeforeRepeatingAtInterva
119122

120123
Within.FiveSeconds(() => firstWork.HasValue.Should().BeTrue());
121124

122-
(firstWork.Value - scheduleTime).Should().BeLessThan(TimeSpan.FromMilliseconds(50));
125+
(firstWork.Value - scheduleTime).Should().BeLessThan(TimeSpan.FromMilliseconds(1000));
123126
}
124127

125-
[Theory]
126-
[InlineData(WorkType.Async)]
127-
[InlineData(WorkType.Sync)]
128-
public void ShouldBeAbleToSpecifyThatWorkIsLongRunning(WorkType workType)
128+
[Fact]
129+
public void ShouldBeAbleToSpecifyThatSyncWorkIsLongRunning()
129130
{
130-
var actorTaskFactory = new ActorTaskFactory();
131-
_actor = Mock.Of<IActor>();
132-
SetUpActor(workType);
133-
_actor.Start();
134-
_scheduler = new ActorWorkScheduler(_actor, actorTaskFactory);
131+
_scheduler.Schedule(() => { }, TimeSpan.FromMilliseconds(1000), ActorScheduleOptions.NoInitialDelay | ActorScheduleOptions.WorkIsLongRunning);
132+
Within.FiveSeconds(
133+
() => Expect.That(
134+
() => Mock.Get(_actorTaskFactory)
135+
.Verify(x => x.Create(It.IsAny<Action<object>>(), It.IsAny<CancellationToken>(), It.Is<TaskCreationOptions>(o => o.HasFlag(TaskCreationOptions.LongRunning)), It.IsAny<object>()), Times.Once))
136+
.ShouldNotThrow());
137+
}
135138

136-
switch (workType)
137-
{
138-
case WorkType.Sync:
139-
{
140-
var work = (Action)(() => { });
141-
_scheduler.Schedule(work, TimeSpan.FromMilliseconds(100), ActorScheduleOptions.NoInitialDelay | ActorScheduleOptions.WorkIsLongRunning);
142-
Within.FiveSeconds(() => Expect.That(() => Mock.Get(_actor).Verify(x => x.Enqueue(work, It.IsAny<CancellationToken>(), ActorEnqueueOptions.WorkIsLongRunning), Times.Once)).ShouldNotThrow());
143-
}
144-
break;
145-
case WorkType.Async:
146-
{
147-
var work = (Func<Task>)(async () => { await Task.Yield(); });
148-
_scheduler.Schedule(work, TimeSpan.FromMilliseconds(100), ActorScheduleOptions.NoInitialDelay | ActorScheduleOptions.WorkIsLongRunning);
149-
Within.FiveSeconds(() => Expect.That(() => Mock.Get(_actor).Verify(x => x.Enqueue(work, It.IsAny<CancellationToken>(), ActorEnqueueOptions.WorkIsLongRunning), Times.Once)).ShouldNotThrow());
150-
}
151-
break;
152-
default:
153-
throw new Exception($"Unhandled test case {workType}.");
154-
}
139+
[Fact]
140+
public void ShouldBeAbleToSpecifyThatAsyncWorkIsLongRunning()
141+
{
142+
_scheduler.Schedule(async () => { await Task.Yield(); }, TimeSpan.FromMilliseconds(1000), ActorScheduleOptions.NoInitialDelay | ActorScheduleOptions.WorkIsLongRunning);
143+
Within.FiveSeconds(
144+
() => Expect.That(
145+
() => Mock.Get(_actorTaskFactory)
146+
.Verify(x => x.Create(It.IsAny<Func<object, Task>>(), It.IsAny<CancellationToken>(), It.Is<TaskCreationOptions>(o => o.HasFlag(TaskCreationOptions.LongRunning)), It.IsAny<object>()), Times.Once))
147+
.ShouldNotThrow());
155148
}
156149

157150
[Theory]
@@ -179,11 +172,11 @@ public void ShouldBeAbleToCancelSchedule(WorkType workType)
179172
throw new Exception($"Unhandled test case {workType}.");
180173
}
181174

182-
Within.OneSecond(() => output.Should().Equal(Enumerable.Repeat("one", 1)));
175+
Within.FiveSeconds(() => output.Should().Equal(Enumerable.Repeat("one", 1)));
183176

184177
_scheduler.CancelCurrent();
185178

186-
Within.OneSecond(() => task.IsCanceled.Should().BeTrue());
179+
Within.FiveSeconds(() => task.IsCanceled.Should().BeTrue());
187180

188181
var marker = output.Count;
189182

@@ -388,45 +381,35 @@ public void AsynchronousSchedulerExtensionShouldEmitAnyArgumentNullExceptions()
388381
.And.ParamName.Should().Be("work");
389382
}
390383

391-
private void SetUpActor(WorkType workType)
384+
private static IActorTaskFactory SetUpTaskFactory()
392385
{
393-
switch (workType)
394-
{
395-
case WorkType.Sync:
396-
Mock.Get(_actor)
397-
.Setup(x => x.Enqueue(It.IsAny<Action>(), It.IsAny<CancellationToken>(), It.IsAny<ActorEnqueueOptions>()))
398-
.Returns<Action, CancellationToken?, ActorEnqueueOptions>((x, y, z) =>
399-
{
400-
try
401-
{
402-
x();
403-
return Task.CompletedTask;
404-
}
405-
catch (Exception exception)
406-
{
407-
return Task.FromException(exception);
408-
}
409-
});
410-
break;
411-
case WorkType.Async:
412-
Mock.Get(_actor)
413-
.Setup(x => x.Enqueue(It.IsAny<Func<Task>>(), It.IsAny<CancellationToken>(), It.IsAny<ActorEnqueueOptions>()))
414-
.Returns<Func<Task>, CancellationToken?, ActorEnqueueOptions>((x, y, z) =>
415-
{
416-
try
417-
{
418-
x().Wait();
419-
return Task.CompletedTask;
420-
}
421-
catch (AggregateException exception)
422-
{
423-
return Task.FromException(exception.InnerExceptions.First());
424-
}
425-
});
426-
break;
427-
default:
428-
throw new Exception($"Unhandled test case {workType}.");
429-
}
386+
var realTaskFactory = new ActorTaskFactory();
387+
var taskFactory = Mock.Of<IActorTaskFactory>();
388+
389+
Mock.Get(taskFactory)
390+
.Setup(x => x.Create(It.IsAny<Action<object>>(), It.IsAny<CancellationToken>(), It.IsAny<TaskCreationOptions>(), It.IsAny<object>()))
391+
.Returns<Action<object>, CancellationToken, TaskCreationOptions, object>((action, cancellationToken, taskCreationOptions, state) => realTaskFactory.Create(action, cancellationToken, taskCreationOptions, state));
392+
Mock.Get(taskFactory)
393+
.Setup(x => x.Create(It.IsAny<Func<object, int>>(), It.IsAny<CancellationToken>(), It.IsAny<TaskCreationOptions>(), It.IsAny<object>()))
394+
.Returns<Func<object, int>, CancellationToken, TaskCreationOptions, object>((function, cancellationToken, taskCreationOptions, state) => realTaskFactory.Create(function, cancellationToken, taskCreationOptions, state));
395+
Mock.Get(taskFactory)
396+
.Setup(x => x.Create(It.IsAny<Func<object, Task>>(), It.IsAny<CancellationToken>(), It.IsAny<TaskCreationOptions>(), It.IsAny<object>()))
397+
.Returns<Func<object, Task>, CancellationToken, TaskCreationOptions, object>((function, cancellationToken, taskCreationOptions, state) => realTaskFactory.Create(function, cancellationToken, taskCreationOptions, state));
398+
Mock.Get(taskFactory)
399+
.Setup(x => x.Create(It.IsAny<Func<object, Task<string>>>(), It.IsAny<CancellationToken>(), It.IsAny<TaskCreationOptions>(), It.IsAny<object>()))
400+
.Returns<Func<object, Task<string>>, CancellationToken, TaskCreationOptions, object>((function, cancellationToken, taskCreationOptions, state) => realTaskFactory.Create(function, cancellationToken, taskCreationOptions, state));
401+
Mock.Get(taskFactory)
402+
.Setup(x => x.FromCompleted())
403+
.Returns(realTaskFactory.FromCompleted);
404+
Mock.Get(taskFactory)
405+
.Setup(x => x.FromException(It.IsAny<Exception>()))
406+
.Returns<Exception>(realTaskFactory.FromException);
407+
Mock.Get(taskFactory)
408+
.Setup(x => x.CreateDelay(It.IsAny<TimeSpan>(), It.IsAny<CancellationToken>()))
409+
.Returns<TimeSpan, CancellationToken>(Task.Delay);
410+
411+
return taskFactory;
430412
}
413+
431414
}
432415
}

0 commit comments

Comments
 (0)