Source file
src/time/tick_test.go
1
2
3
4
5 package time_test
6
7 import (
8 "fmt"
9 "runtime"
10 "sync"
11 "testing"
12 . "time"
13 )
14
15 func TestTicker(t *testing.T) {
16 t.Parallel()
17
18
19
20
21
22
23
24 baseCount := 10
25 baseDelta := 20 * Millisecond
26
27
28 if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
29
30
31
32
33
34 baseCount = 6
35 baseDelta = 100 * Millisecond
36 }
37
38 var errs []string
39 logErrs := func() {
40 for _, e := range errs {
41 t.Log(e)
42 }
43 }
44
45 for _, test := range []struct {
46 count int
47 delta Duration
48 }{{
49 count: baseCount,
50 delta: baseDelta,
51 }, {
52 count: 8,
53 delta: 1 * Second,
54 }} {
55 count, delta := test.count, test.delta
56 ticker := NewTicker(delta)
57 t0 := Now()
58 for range count / 2 {
59 <-ticker.C
60 }
61 ticker.Reset(delta * 2)
62 for range count - count/2 {
63 <-ticker.C
64 }
65 ticker.Stop()
66 t1 := Now()
67 dt := t1.Sub(t0)
68 target := 3 * delta * Duration(count/2)
69 slop := target * 3 / 10
70 if dt < target-slop || dt > target+slop {
71 errs = append(errs, fmt.Sprintf("%d %s ticks then %d %s ticks took %s, expected [%s,%s]", count/2, delta, count/2, delta*2, dt, target-slop, target+slop))
72 if dt > target+slop {
73
74
75 Sleep(Second / 2)
76 }
77 continue
78 }
79
80 Sleep(2 * delta)
81 select {
82 case <-ticker.C:
83 errs = append(errs, "Ticker did not shut down")
84 continue
85 default:
86
87 }
88
89
90 if len(errs) > 0 {
91 t.Logf("saw %d errors, ignoring to avoid flakiness", len(errs))
92 logErrs()
93 }
94
95 return
96 }
97
98 t.Errorf("saw %d errors", len(errs))
99 logErrs()
100 }
101
102
103 func TestTickerStopWithDirectInitialization(t *testing.T) {
104 c := make(chan Time)
105 tk := &Ticker{C: c}
106 tk.Stop()
107 }
108
109
110 func TestTeardown(t *testing.T) {
111 t.Parallel()
112
113 Delta := 100 * Millisecond
114 if testing.Short() {
115 Delta = 20 * Millisecond
116 }
117 for range 3 {
118 ticker := NewTicker(Delta)
119 <-ticker.C
120 ticker.Stop()
121 }
122 }
123
124
125 func TestTick(t *testing.T) {
126
127 if got := Tick(-1); got != nil {
128 t.Errorf("Tick(-1) = %v; want nil", got)
129 }
130 }
131
132
133 func TestNewTickerLtZeroDuration(t *testing.T) {
134 defer func() {
135 if err := recover(); err == nil {
136 t.Errorf("NewTicker(-1) should have panicked")
137 }
138 }()
139 NewTicker(-1)
140 }
141
142
143 func TestTickerResetLtZeroDuration(t *testing.T) {
144 defer func() {
145 if err := recover(); err == nil {
146 t.Errorf("Ticker.Reset(0) should have panicked")
147 }
148 }()
149 tk := NewTicker(Second)
150 tk.Reset(0)
151 }
152
153 func TestLongAdjustTimers(t *testing.T) {
154 if runtime.GOOS == "android" || runtime.GOOS == "ios" || runtime.GOOS == "plan9" {
155 t.Skipf("skipping on %s - too slow", runtime.GOOS)
156 }
157 if testing.Short() && runtime.NumCPU() < 2 {
158 t.Skipf("skipping in short mode, insufficient CPUs")
159 }
160
161 t.Parallel()
162 var wg sync.WaitGroup
163 defer wg.Wait()
164
165
166 const count = 5000
167 wg.Add(count)
168 for range count {
169 go func() {
170 defer wg.Done()
171 Sleep(10 * Microsecond)
172 }()
173 }
174 for range count {
175 Sleep(1 * Microsecond)
176 }
177
178
179
180
181
182
183 done := make(chan bool)
184 AfterFunc(60*Second, func() { close(done) })
185
186
187 inQ := make(chan func())
188 outQ := make(chan func())
189
190 defer close(inQ)
191
192 wg.Add(1)
193 go func() {
194 defer wg.Done()
195 defer close(outQ)
196 var q []func()
197 for {
198 var sendTo chan func()
199 var send func()
200 if len(q) > 0 {
201 sendTo = outQ
202 send = q[0]
203 }
204 select {
205 case sendTo <- send:
206 q = q[1:]
207 case f, ok := <-inQ:
208 if !ok {
209 return
210 }
211 q = append(q, f)
212 case <-done:
213 return
214 }
215 }
216 }()
217
218 for i := range 50000 {
219 const try = 20
220 for range try {
221 inQ <- func() {}
222 }
223 for range try {
224 select {
225 case _, ok := <-outQ:
226 if !ok {
227 t.Fatal("output channel is closed")
228 }
229 case <-After(5 * Second):
230 t.Fatalf("failed to read work, iteration %d", i)
231 case <-done:
232 t.Fatal("timer expired")
233 }
234 }
235 }
236 }
237 func BenchmarkTicker(b *testing.B) {
238 benchmark(b, func(pb *testing.PB) {
239 ticker := NewTicker(Nanosecond)
240 for pb.Next() {
241 <-ticker.C
242 }
243 ticker.Stop()
244 })
245 }
246
247 func BenchmarkTickerReset(b *testing.B) {
248 benchmark(b, func(pb *testing.PB) {
249 ticker := NewTicker(Nanosecond)
250 for pb.Next() {
251 ticker.Reset(Nanosecond * 2)
252 }
253 ticker.Stop()
254 })
255 }
256
257 func BenchmarkTickerResetNaive(b *testing.B) {
258 benchmark(b, func(pb *testing.PB) {
259 ticker := NewTicker(Nanosecond)
260 for pb.Next() {
261 ticker.Stop()
262 ticker = NewTicker(Nanosecond * 2)
263 }
264 ticker.Stop()
265 })
266 }
267
268 func TestTimerGC(t *testing.T) {
269 run := func(t *testing.T, what string, f func()) {
270 t.Helper()
271 t.Run(what, func(t *testing.T) {
272 t.Helper()
273 const N = 1e4
274 var stats runtime.MemStats
275 runtime.GC()
276 runtime.GC()
277 runtime.GC()
278 runtime.ReadMemStats(&stats)
279 before := int64(stats.Mallocs - stats.Frees)
280
281 for j := 0; j < N; j++ {
282 f()
283 }
284
285 runtime.GC()
286 runtime.GC()
287 runtime.GC()
288 runtime.ReadMemStats(&stats)
289 after := int64(stats.Mallocs - stats.Frees)
290
291
292 inuse := after - before
293 if inuse >= N {
294 t.Errorf("%s did not get GC'ed: %d allocations", what, inuse)
295
296 Sleep(1 * Second)
297 runtime.ReadMemStats(&stats)
298 after := int64(stats.Mallocs - stats.Frees)
299 inuse = after - before
300 t.Errorf("after a sleep: %d allocations", inuse)
301 }
302 })
303 }
304
305 run(t, "After", func() { After(Hour) })
306 run(t, "Tick", func() { Tick(Hour) })
307 run(t, "NewTimer", func() { NewTimer(Hour) })
308 run(t, "NewTicker", func() { NewTicker(Hour) })
309 run(t, "NewTimerStop", func() { NewTimer(Hour).Stop() })
310 run(t, "NewTickerStop", func() { NewTicker(Hour).Stop() })
311 }
312
313 func TestChan(t *testing.T) {
314 t.Run("Timer", func(t *testing.T) {
315 tim := NewTimer(10000 * Second)
316 testTimerChan(t, tim, tim.C)
317 })
318 t.Run("Ticker", func(t *testing.T) {
319 tim := &tickerTimer{Ticker: NewTicker(10000 * Second)}
320 testTimerChan(t, tim, tim.C)
321 })
322 }
323
324 type timer interface {
325 Stop() bool
326 Reset(Duration) bool
327 }
328
329
330
331 type tickerTimer struct {
332 *Ticker
333 stopped bool
334 }
335
336 func (t *tickerTimer) Stop() bool {
337 pending := !t.stopped
338 t.stopped = true
339 t.Ticker.Stop()
340 return pending
341 }
342
343 func (t *tickerTimer) Reset(d Duration) bool {
344 pending := !t.stopped
345 t.stopped = false
346 t.Ticker.Reset(d)
347 return pending
348 }
349
350 func testTimerChan(t *testing.T, tim timer, C <-chan Time) {
351 _, isTimer := tim.(*Timer)
352 isTicker := !isTimer
353
354
355
356
357 const (
358 sched = 10 * Millisecond
359 tries = 100
360 drainTries = 5
361 )
362
363 noTick := func() {
364 t.Helper()
365 select {
366 default:
367 case <-C:
368 t.Errorf("extra tick")
369 }
370 }
371 assertTick := func() {
372 t.Helper()
373 select {
374 default:
375 case <-C:
376 return
377 }
378 for range tries {
379 Sleep(sched)
380 select {
381 default:
382 case <-C:
383 return
384 }
385 }
386 t.Errorf("missing tick")
387 }
388
389
390 tim.Stop()
391 noTick()
392
393
394 tim.Reset(10000 * Second)
395 noTick()
396
397
398 tim.Reset(1)
399 Sleep(sched)
400 if l, c := len(C), cap(C); l != 0 || c != 0 {
401 t.Fatalf("len(C), cap(C) = %d, %d, want 0, 0", l, c)
402 }
403 assertTick()
404
405
406
407 Sleep(sched)
408 tim.Reset(10000 * Second)
409 noTick()
410
411 notDone := func(done chan bool) {
412 t.Helper()
413 select {
414 default:
415 case <-done:
416 t.Fatalf("early done")
417 }
418 }
419
420 waitDone := func(done chan bool) {
421 t.Helper()
422 for range tries {
423 Sleep(sched)
424 select {
425 case <-done:
426 return
427 default:
428 }
429 }
430 t.Fatalf("never got done")
431 }
432
433
434 tim.Reset(10000 * Second)
435
436
437 done := make(chan bool)
438 notDone(done)
439 go func() {
440 <-C
441 close(done)
442 }()
443 Sleep(sched)
444 notDone(done)
445
446
447 tim.Reset(20000 * Second)
448 Sleep(sched)
449 notDone(done)
450
451
452 tim.Reset(1)
453 waitDone(done)
454
455
456
457 if isTicker {
458 assertTick()
459 } else {
460 noTick()
461 }
462
463 tim.Stop()
464 noTick()
465
466
467 tim.Reset(10000 * Second)
468 done = make(chan bool, 2)
469 done1 := make(chan bool)
470 done2 := make(chan bool)
471 stop := make(chan bool)
472 go func() {
473 select {
474 case <-C:
475 done <- true
476 case <-stop:
477 }
478 close(done1)
479 }()
480 go func() {
481 select {
482 case <-C:
483 done <- true
484 case <-stop:
485 }
486 close(done2)
487 }()
488 Sleep(sched)
489 notDone(done)
490 tim.Reset(sched / 2)
491 Sleep(sched)
492 waitDone(done)
493 tim.Stop()
494 close(stop)
495 waitDone(done1)
496 waitDone(done2)
497 if isTicker {
498
499
500 select {
501 default:
502 case <-done:
503 }
504
505 select {
506 default:
507 case <-C:
508 }
509 }
510 notDone(done)
511
512
513 stop = make(chan bool)
514 done = make(chan bool, 2)
515 for range 2 {
516 go func() {
517 select {
518 case <-C:
519 panic("unexpected data")
520 case <-stop:
521 }
522 done <- true
523 }()
524 }
525 Sleep(sched)
526 close(stop)
527 waitDone(done)
528 waitDone(done)
529
530
531
532 tim.Reset(1)
533 Sleep(10 * Millisecond)
534 if pending := tim.Stop(); pending != true {
535 t.Errorf("tim.Stop() = %v, want true", pending)
536 }
537 noTick()
538
539 tim.Reset(Hour)
540 noTick()
541 if pending := tim.Reset(1); pending != true {
542 t.Errorf("tim.Stop() = %v, want true", pending)
543 }
544 assertTick()
545 Sleep(10 * Millisecond)
546 if isTicker {
547 assertTick()
548 Sleep(10 * Millisecond)
549 } else {
550 noTick()
551 }
552 if pending, want := tim.Reset(Hour), isTicker; pending != want {
553 t.Errorf("tim.Stop() = %v, want %v", pending, want)
554 }
555 noTick()
556 }
557
558 func TestManualTicker(t *testing.T) {
559
560
561
562 c := make(chan Time)
563 tick := &Ticker{C: c}
564 tick.Stop()
565 }
566
567 func TestAfterTimes(t *testing.T) {
568 t.Parallel()
569
570
571
572
573
574 for range 10 {
575 start := Now()
576 c := After(10 * Millisecond)
577 Sleep(500 * Millisecond)
578 dt := (<-c).Sub(start)
579 if dt < 400*Millisecond {
580 return
581 }
582 t.Logf("After(10ms) time is +%v, want <400ms", dt)
583 }
584 t.Errorf("not working")
585 }
586
587 func TestTickTimes(t *testing.T) {
588 t.Parallel()
589
590 for range 10 {
591 start := Now()
592 c := Tick(10 * Millisecond)
593 Sleep(500 * Millisecond)
594 dt := (<-c).Sub(start)
595 if dt < 400*Millisecond {
596 return
597 }
598 t.Logf("Tick(10ms) time is +%v, want <400ms", dt)
599 }
600 t.Errorf("not working")
601 }
602
View as plain text