@@ -2,6 +2,7 @@ package quartz_test
2
2
3
3
import (
4
4
"context"
5
+ "errors"
5
6
"testing"
6
7
"time"
7
8
@@ -210,3 +211,107 @@ func TestPeek(t *testing.T) {
210
211
t .Fatal ("expected Peek() to return false" )
211
212
}
212
213
}
214
+
215
+ // TestTickerFunc_ContextDoneDuringTick tests that TickerFunc.Wait() can't return while the tick
216
+ // function callback is in progress.
217
+ func TestTickerFunc_ContextDoneDuringTick (t * testing.T ) {
218
+ t .Parallel ()
219
+ testCtx , testCancel := context .WithTimeout (context .Background (), 10 * time .Second )
220
+ defer testCancel ()
221
+ mClock := quartz .NewMock (t )
222
+
223
+ tickStart := make (chan struct {})
224
+ tickDone := make (chan struct {})
225
+ ctx , cancel := context .WithCancel (testCtx )
226
+ defer cancel ()
227
+ tkr := mClock .TickerFunc (ctx , time .Second , func () error {
228
+ close (tickStart )
229
+ select {
230
+ case <- tickDone :
231
+ case <- testCtx .Done ():
232
+ t .Error ("timeout waiting for tickDone" )
233
+ }
234
+ return nil
235
+ })
236
+ w := mClock .Advance (time .Second )
237
+ select {
238
+ case <- tickStart :
239
+ // OK
240
+ case <- testCtx .Done ():
241
+ t .Fatal ("timeout waiting for tickStart" )
242
+ }
243
+ waitErr := make (chan error , 1 )
244
+ go func () {
245
+ waitErr <- tkr .Wait ()
246
+ }()
247
+ cancel ()
248
+ select {
249
+ case <- waitErr :
250
+ t .Fatal ("wait should not return while tick callback in progress" )
251
+ case <- time .After (time .Millisecond * 100 ):
252
+ // OK
253
+ }
254
+ close (tickDone )
255
+ select {
256
+ case err := <- waitErr :
257
+ if ! errors .Is (err , context .Canceled ) {
258
+ t .Fatal ("expected context.Canceled error" )
259
+ }
260
+ case <- testCtx .Done ():
261
+ t .Fatal ("timed out waiting for wait to finish" )
262
+ }
263
+ w .MustWait (testCtx )
264
+ }
265
+
266
+ // TestTickerFunc_LongCallback tests that we don't call the ticker func a second time while the
267
+ // first is still executing.
268
+ func TestTickerFunc_LongCallback (t * testing.T ) {
269
+ t .Parallel ()
270
+ testCtx , testCancel := context .WithTimeout (context .Background (), 10 * time .Second )
271
+ defer testCancel ()
272
+ mClock := quartz .NewMock (t )
273
+
274
+ expectedErr := errors .New ("callback error" )
275
+ tickStart := make (chan struct {})
276
+ tickDone := make (chan struct {})
277
+ ctx , cancel := context .WithCancel (testCtx )
278
+ defer cancel ()
279
+ tkr := mClock .TickerFunc (ctx , time .Second , func () error {
280
+ close (tickStart )
281
+ select {
282
+ case <- tickDone :
283
+ case <- testCtx .Done ():
284
+ t .Error ("timeout waiting for tickDone" )
285
+ }
286
+ return expectedErr
287
+ })
288
+ w := mClock .Advance (time .Second )
289
+ select {
290
+ case <- tickStart :
291
+ // OK
292
+ case <- testCtx .Done ():
293
+ t .Fatal ("timeout waiting for tickStart" )
294
+ }
295
+ // second tick completes immediately, since it doesn't actually call the
296
+ // ticker function.
297
+ mClock .Advance (time .Second ).MustWait (testCtx )
298
+
299
+ waitErr := make (chan error , 1 )
300
+ go func () {
301
+ waitErr <- tkr .Wait ()
302
+ }()
303
+ cancel ()
304
+ close (tickDone )
305
+
306
+ select {
307
+ case err := <- waitErr :
308
+ // we should get the function error, not the context error, since context was canceled while
309
+ // we were calling the function, and it returned an error.
310
+ if ! errors .Is (err , expectedErr ) {
311
+ t .Fatalf ("wrong error: %s" , err )
312
+ }
313
+ case <- testCtx .Done ():
314
+ t .Fatal ("timed out waiting for wait to finish" )
315
+ }
316
+ w .MustWait (testCtx )
317
+ }
0 commit comments