@@ -2,6 +2,7 @@ package quartz_test
22
33import (
44 "context"
5+ "errors"
56 "testing"
67 "time"
78
@@ -210,3 +211,107 @@ func TestPeek(t *testing.T) {
210211 t .Fatal ("expected Peek() to return false" )
211212 }
212213}
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