1414
1515namespace 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