@@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Hosting.Tests
11
11
public class BackgroundServiceTests
12
12
{
13
13
[ Fact ]
14
- public void StartReturnsCompletedTaskIfLongRunningTaskIsIncomplete ( )
14
+ public void StartReturnsCompletedTask ( )
15
15
{
16
16
var tcs = new TaskCompletionSource < object > ( ) ;
17
17
var service = new MyBackgroundService ( tcs . Task ) ;
@@ -26,28 +26,12 @@ public void StartReturnsCompletedTaskIfLongRunningTaskIsIncomplete()
26
26
}
27
27
28
28
[ Fact ]
29
- public void StartReturnsCompletedTaskIfCancelled ( )
29
+ public async Task StartCancelledThrowsTaskCanceledException ( )
30
30
{
31
- var tcs = new TaskCompletionSource < object > ( ) ;
32
- tcs . TrySetCanceled ( ) ;
33
- var service = new MyBackgroundService ( tcs . Task ) ;
34
-
35
- var task = service . StartAsync ( CancellationToken . None ) ;
36
-
37
- Assert . True ( task . IsCompleted ) ;
38
- Assert . Same ( task , service . ExecuteTask ) ;
39
- }
40
-
41
- [ Fact ]
42
- public async Task StartReturnsLongRunningTaskIfFailed ( )
43
- {
44
- var tcs = new TaskCompletionSource < object > ( ) ;
45
- tcs . TrySetException ( new Exception ( "fail!" ) ) ;
46
- var service = new MyBackgroundService ( tcs . Task ) ;
47
-
48
- var exception = await Assert . ThrowsAsync < Exception > ( ( ) => service . StartAsync ( CancellationToken . None ) ) ;
31
+ var ct = new CancellationToken ( true ) ;
32
+ var service = new WaitForCancelledTokenService ( ) ;
49
33
50
- Assert . Equal ( "fail!" , exception . Message ) ;
34
+ await Assert . ThrowsAsync < TaskCanceledException > ( ( ) => service . StartAsync ( ct ) ) ;
51
35
}
52
36
53
37
[ Fact ]
@@ -116,6 +100,7 @@ public async Task StartAsyncThenCancelShouldCancelExecutingTask()
116
100
var service = new WaitForCancelledTokenService ( ) ;
117
101
118
102
await service . StartAsync ( tokenSource . Token ) ;
103
+ await service . WaitForExecuteTask ;
119
104
120
105
tokenSource . Cancel ( ) ;
121
106
@@ -130,13 +115,48 @@ public void CreateAndDisposeShouldNotThrow()
130
115
service . Dispose ( ) ;
131
116
}
132
117
118
+ [ Fact ]
119
+ public async Task StartSynchronousAndStop ( )
120
+ {
121
+ var tokenSource = new CancellationTokenSource ( ) ;
122
+ var service = new MySynchronousBackgroundService ( ) ;
123
+
124
+ // should not block the start thread;
125
+ await service . StartAsync ( tokenSource . Token ) ;
126
+ await service . WaitForExecuteTask ;
127
+ await service . StopAsync ( CancellationToken . None ) ;
128
+
129
+ Assert . True ( service . WaitForEndExecuteTask . IsCompleted ) ;
130
+ }
131
+
132
+ [ Fact ]
133
+ public async Task StartSynchronousExecuteShouldBeCancelable ( )
134
+ {
135
+ var tokenSource = new CancellationTokenSource ( ) ;
136
+ var service = new MySynchronousBackgroundService ( ) ;
137
+
138
+ await service . StartAsync ( tokenSource . Token ) ;
139
+ await service . WaitForExecuteTask ;
140
+
141
+ tokenSource . Cancel ( ) ;
142
+
143
+ await service . WaitForEndExecuteTask ;
144
+ }
145
+
133
146
private class WaitForCancelledTokenService : BackgroundService
134
147
{
148
+ private TaskCompletionSource < object > _waitForExecuteTask = new TaskCompletionSource < object > ( ) ;
149
+
135
150
public Task ExecutingTask { get ; private set ; }
136
151
152
+ public Task WaitForExecuteTask => _waitForExecuteTask . Task ;
153
+
137
154
protected override Task ExecuteAsync ( CancellationToken stoppingToken )
138
155
{
139
156
ExecutingTask = Task . Delay ( Timeout . Infinite , stoppingToken ) ;
157
+
158
+ _waitForExecuteTask . TrySetResult ( null ) ;
159
+
140
160
return ExecutingTask ;
141
161
}
142
162
}
@@ -191,5 +211,27 @@ private async Task ExecuteCore(CancellationToken stoppingToken)
191
211
await task ;
192
212
}
193
213
}
214
+
215
+ private class MySynchronousBackgroundService : BackgroundService
216
+ {
217
+ private TaskCompletionSource < object > _waitForExecuteTask = new TaskCompletionSource < object > ( ) ;
218
+ private TaskCompletionSource < object > _waitForEndExecuteTask = new TaskCompletionSource < object > ( ) ;
219
+
220
+ public Task WaitForExecuteTask => _waitForExecuteTask . Task ;
221
+ public Task WaitForEndExecuteTask => _waitForEndExecuteTask . Task ;
222
+
223
+ #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
224
+ protected override async Task ExecuteAsync ( CancellationToken stoppingToken )
225
+ {
226
+ _waitForExecuteTask . TrySetResult ( null ) ;
227
+ while ( ! stoppingToken . IsCancellationRequested )
228
+ {
229
+ Thread . Sleep ( 100 ) ; // never await, just block the thread
230
+ }
231
+ _waitForEndExecuteTask . TrySetResult ( null ) ;
232
+ }
233
+ #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
234
+
235
+ }
194
236
}
195
237
}
0 commit comments