44using System ;
55using System . Collections . Generic ;
66using System . Linq ;
7- using System . Threading ;
87using System . Threading . Tasks ;
9- using Moq ;
108using FluentAssertions ;
11- using Winton . Extensions . Threading . Actor . Internal ;
129using Winton . Extensions . Threading . Actor . Tests . Utilities ;
1310using Xunit ;
1411
@@ -24,20 +21,18 @@ public enum WorkType
2421
2522 private readonly IActor _actor ;
2623 private readonly IActorWorkScheduler _scheduler ;
27- private readonly IActorTaskFactory _actorTaskFactory ;
2824
2925 public ActorWorkSchedulerTests ( )
3026 {
31- _actorTaskFactory = SetUpTaskFactory ( ) ;
32- _actor = new Actor ( _actorTaskFactory ) ;
27+ _actor = new Actor ( ) ;
3328 _actor . Start ( ) ;
34- _scheduler = new ActorWorkScheduler ( _actor , _actorTaskFactory ) ;
29+ _scheduler = _actor . CreateWorkScheduler ( ) ;
3530 }
3631
3732 public void Dispose ( )
3833 {
34+ _actor . Stop ( ) . Wait ( ) ;
3935 _scheduler . CancelCurrent ( ) ;
40- _actor . Stop ( ) ;
4136 }
4237
4338 [ Theory ]
@@ -52,31 +47,30 @@ public void ShouldBeAbleToScheduleWorkToRepeatAtAFixedInterval(WorkType workType
5247 var times = new List < DateTime > ( ) ;
5348 var sampleSize = 5 ;
5449
55- Action adder =
56- ( ) =>
50+ void Adder ( )
51+ {
52+ if ( times . Count < sampleSize )
5753 {
58- if ( times . Count < sampleSize )
59- {
60- times . Add ( DateTime . UtcNow ) ;
61- }
62-
63- if ( times . Count == sampleSize )
64- {
65- // Block here so that we can assess something that's not moving
66- barrier . Task . Wait ( ) ;
67- }
68- } ;
54+ times . Add ( DateTime . UtcNow ) ;
55+ }
56+
57+ if ( times . Count == sampleSize )
58+ {
59+ // Block here so that we can assess something that's not moving
60+ barrier . Task . Wait ( ) ;
61+ }
62+ }
6963
7064 switch ( workType )
7165 {
7266 case WorkType . Sync :
73- _scheduler . Schedule ( adder , expectedInterval , actorScheduleOptions ) ;
67+ _scheduler . Schedule ( ( Action ) Adder , expectedInterval , actorScheduleOptions ) ;
7468 break ;
7569 case WorkType . Async :
7670 _scheduler . Schedule ( async ( ) =>
7771 {
7872 await Task . Yield ( ) ;
79- adder ( ) ;
73+ Adder ( ) ;
8074 } ,
8175 expectedInterval ,
8276 actorScheduleOptions ) ;
@@ -92,35 +86,53 @@ public void ShouldBeAbleToScheduleWorkToRepeatAtAFixedInterval(WorkType workType
9286 // Use 75ms instead of 100ms to give it a bit of latitude: mainly we just want to make sure there is some delaying going on.
9387 actualIntervals . Should ( ) . OnlyContain ( x => x >= TimeSpan . FromMilliseconds ( 75 ) ) ;
9488
95- var expectedNumberOfDelays = sampleSize - ( actorScheduleOptions . HasFlag ( ActorScheduleOptions . NoInitialDelay ) ? 1 : 0 ) ;
96-
97- Expect . That ( ( ) => Mock . Get ( _actorTaskFactory )
98- . Verify ( x => x . CreateDelay ( expectedInterval , It . IsAny < CancellationToken > ( ) ) , Times . Exactly ( expectedNumberOfDelays ) ) )
99- . ShouldNotThrow ( ) ;
100-
10189 barrier . SetResult ( true ) ;
10290 }
10391
10492 [ Fact ]
10593 public void ShouldBeAbleToSpecifyThatSyncWorkIsLongRunning ( )
10694 {
107- _scheduler . Schedule ( ( ) => { } , TimeSpan . FromMilliseconds ( 1000 ) , ActorScheduleOptions . NoInitialDelay | ActorScheduleOptions . WorkIsLongRunning ) ;
108- Within . FiveSeconds (
109- ( ) => Expect . That (
110- ( ) => Mock . Get ( _actorTaskFactory )
111- . Verify ( x => x . Create ( It . IsAny < Action < object > > ( ) , It . IsAny < CancellationToken > ( ) , It . Is < TaskCreationOptions > ( o => o . HasFlag ( TaskCreationOptions . LongRunning ) ) , It . IsAny < object > ( ) ) , Times . Once ) )
112- . ShouldNotThrow ( ) ) ;
95+ var taskCompletionSource = new TaskCompletionSource < object > ( ) ;
96+
97+ _scheduler . Schedule (
98+ ( ) =>
99+ {
100+ try
101+ {
102+ ActorThreadAssertions . CurrentThreadShouldNotBeThreadPoolThread ( ) ;
103+ taskCompletionSource . TrySetResult ( null ) ;
104+ }
105+ catch ( Exception exception )
106+ {
107+ taskCompletionSource . TrySetException ( exception ) ;
108+ }
109+ } , TimeSpan . FromMilliseconds ( 1000 ) , ActorScheduleOptions . NoInitialDelay | ActorScheduleOptions . WorkIsLongRunning ) ;
110+
111+ taskCompletionSource . Awaiting ( async x => await x . Task ) . ShouldNotThrow ( ) ;
113112 }
114113
115114 [ Fact ]
116115 public void ShouldBeAbleToSpecifyThatAsyncWorkIsLongRunning ( )
117116 {
118- _scheduler . Schedule ( async ( ) => { await Task . Yield ( ) ; } , TimeSpan . FromMilliseconds ( 1000 ) , ActorScheduleOptions . NoInitialDelay | ActorScheduleOptions . WorkIsLongRunning ) ;
119- Within . FiveSeconds (
120- ( ) => Expect . That (
121- ( ) => Mock . Get ( _actorTaskFactory )
122- . 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 ) )
123- . ShouldNotThrow ( ) ) ;
117+ var taskCompletionSource = new TaskCompletionSource < object > ( ) ;
118+
119+ _scheduler . Schedule (
120+ async ( ) =>
121+ {
122+ await Task . Yield ( ) ;
123+
124+ try
125+ {
126+ ActorThreadAssertions . CurrentThreadShouldNotBeThreadPoolThread ( ) ;
127+ taskCompletionSource . TrySetResult ( null ) ;
128+ }
129+ catch ( Exception exception )
130+ {
131+ taskCompletionSource . TrySetException ( exception ) ;
132+ }
133+ } , TimeSpan . FromMilliseconds ( 1000 ) , ActorScheduleOptions . NoInitialDelay | ActorScheduleOptions . WorkIsLongRunning ) ;
134+
135+ taskCompletionSource . Awaiting ( async x => await x . Task ) . ShouldNotThrow ( ) ;
124136 }
125137
126138 [ Theory ]
@@ -172,31 +184,30 @@ public void ASecondCallToScheduleShouldCancelTheWorkPreviouslyScheduled(WorkType
172184 var firstTwoAddedPromise = new TaskCompletionSource < bool > ( ) ;
173185 var gotAOneAfterATwoPromise = new TaskCompletionSource < bool > ( ) ;
174186
175- Action < string > adder =
176- x =>
177- {
178- output . Add ( x ) ;
187+ void Adder ( string x )
188+ {
189+ output . Add ( x ) ;
179190
180- if ( x == "two" )
181- {
182- firstTwoAddedPromise . TrySetResult ( true ) ;
183- }
184- else if ( firstTwoAddedPromise . Task . IsCompleted && x == "one" )
185- {
186- gotAOneAfterATwoPromise . TrySetResult ( true ) ;
187- }
188- } ;
191+ if ( x == "two" )
192+ {
193+ firstTwoAddedPromise . TrySetResult ( true ) ;
194+ }
195+ else if ( firstTwoAddedPromise . Task . IsCompleted && x == "one" )
196+ {
197+ gotAOneAfterATwoPromise . TrySetResult ( true ) ;
198+ }
199+ }
189200
190201 switch ( workType1 )
191202 {
192203 case WorkType . Sync :
193- task1 = _scheduler . Schedule ( ( ) => { adder ( "one" ) ; } , interval ) ;
204+ task1 = _scheduler . Schedule ( ( ) => { Adder ( "one" ) ; } , interval ) ;
194205 break ;
195206 case WorkType . Async :
196207 task1 = _scheduler . Schedule ( async ( ) =>
197208 {
198- await Task . Yield ( ) ;
199- adder ( "one" ) ;
209+ await Task . Delay ( TimeSpan . FromMilliseconds ( 10 ) ) ;
210+ Adder ( "one" ) ;
200211 } , interval ) ;
201212 break ;
202213 default :
@@ -208,12 +219,12 @@ public void ASecondCallToScheduleShouldCancelTheWorkPreviouslyScheduled(WorkType
208219 switch ( workType2 )
209220 {
210221 case WorkType . Sync :
211- _scheduler . Schedule ( ( ) => { adder ( "two" ) ; } , interval ) ;
222+ _scheduler . Schedule ( ( ) => { Adder ( "two" ) ; } , interval ) ;
212223 break ;
213224 case WorkType . Async :
214225 _scheduler . Schedule ( async ( ) =>
215226 {
216- adder ( "two" ) ;
227+ Adder ( "two" ) ;
217228 await Task . Yield ( ) ;
218229 } , interval ) ;
219230 break ;
@@ -348,40 +359,9 @@ public void AsynchronousSchedulerExtensionShouldEmitAnyArgumentOutOfRangeExcepti
348359 [ Fact ]
349360 public void AsynchronousSchedulerExtensionShouldEmitAnyArgumentNullExceptions ( )
350361 {
351- Expect . That ( ( ) => _scheduler . Schedule ( ( Func < Task > ) null , TimeSpan . FromDays ( 1 ) , ActorScheduleOptions . Default , x => { } ) )
362+ Expect . That ( ( ) => _scheduler . Schedule ( null , TimeSpan . FromDays ( 1 ) , ActorScheduleOptions . Default , x => { } ) )
352363 . ShouldThrow < ArgumentNullException > ( )
353364 . And . ParamName . Should ( ) . Be ( "work" ) ;
354365 }
355-
356- private static IActorTaskFactory SetUpTaskFactory ( )
357- {
358- var realTaskFactory = new ActorTaskFactory ( ) ;
359- var taskFactory = Mock . Of < IActorTaskFactory > ( ) ;
360-
361- Mock . Get ( taskFactory )
362- . Setup ( x => x . Create ( It . IsAny < Action < object > > ( ) , It . IsAny < CancellationToken > ( ) , It . IsAny < TaskCreationOptions > ( ) , It . IsAny < object > ( ) ) )
363- . Returns < Action < object > , CancellationToken , TaskCreationOptions , object > ( ( action , cancellationToken , taskCreationOptions , state ) => realTaskFactory . Create ( action , cancellationToken , taskCreationOptions , state ) ) ;
364- Mock . Get ( taskFactory )
365- . Setup ( x => x . Create ( It . IsAny < Func < object , int > > ( ) , It . IsAny < CancellationToken > ( ) , It . IsAny < TaskCreationOptions > ( ) , It . IsAny < object > ( ) ) )
366- . Returns < Func < object , int > , CancellationToken , TaskCreationOptions , object > ( ( function , cancellationToken , taskCreationOptions , state ) => realTaskFactory . Create ( function , cancellationToken , taskCreationOptions , state ) ) ;
367- Mock . Get ( taskFactory )
368- . Setup ( x => x . Create ( It . IsAny < Func < object , Task > > ( ) , It . IsAny < CancellationToken > ( ) , It . IsAny < TaskCreationOptions > ( ) , It . IsAny < object > ( ) ) )
369- . Returns < Func < object , Task > , CancellationToken , TaskCreationOptions , object > ( ( function , cancellationToken , taskCreationOptions , state ) => realTaskFactory . Create ( function , cancellationToken , taskCreationOptions , state ) ) ;
370- Mock . Get ( taskFactory )
371- . Setup ( x => x . Create ( It . IsAny < Func < object , Task < string > > > ( ) , It . IsAny < CancellationToken > ( ) , It . IsAny < TaskCreationOptions > ( ) , It . IsAny < object > ( ) ) )
372- . Returns < Func < object , Task < string > > , CancellationToken , TaskCreationOptions , object > ( ( function , cancellationToken , taskCreationOptions , state ) => realTaskFactory . Create ( function , cancellationToken , taskCreationOptions , state ) ) ;
373- Mock . Get ( taskFactory )
374- . Setup ( x => x . FromCompleted ( ) )
375- . Returns ( realTaskFactory . FromCompleted ) ;
376- Mock . Get ( taskFactory )
377- . Setup ( x => x . FromException ( It . IsAny < Exception > ( ) ) )
378- . Returns < Exception > ( realTaskFactory . FromException ) ;
379- Mock . Get ( taskFactory )
380- . Setup ( x => x . CreateDelay ( It . IsAny < TimeSpan > ( ) , It . IsAny < CancellationToken > ( ) ) )
381- . Returns < TimeSpan , CancellationToken > ( Task . Delay ) ;
382-
383- return taskFactory ;
384- }
385-
386366 }
387367}
0 commit comments