mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: tests for ticker (#47986)
This commit is contained in:
parent
4fbc481d1f
commit
53a6c0210d
@ -1,121 +1,119 @@
|
||||
package alerting
|
||||
|
||||
// import (
|
||||
// "testing"
|
||||
// "time"
|
||||
//
|
||||
// "github.com/benbjohnson/clock"
|
||||
// )
|
||||
//
|
||||
// func inspectTick(tick time.Time, last time.Time, offset time.Duration, t *testing.T) {
|
||||
// if !tick.Equal(last.Add(time.Duration(1) * time.Second)) {
|
||||
// t.Fatalf("expected a tick 1 second more than prev, %s. got: %s", last, tick)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// returns the new last tick seen
|
||||
// func assertAdvanceUntil(ticker *Ticker, last, desiredLast time.Time, offset, wait time.Duration, t *testing.T) time.Time {
|
||||
// for {
|
||||
// select {
|
||||
// case tick := <-ticker.C:
|
||||
// inspectTick(tick, last, offset, t)
|
||||
// last = tick
|
||||
// case <-time.NewTimer(wait).C:
|
||||
// if last.Before(desiredLast) {
|
||||
// t.Fatalf("waited %s for ticker to advance to %s, but only went up to %s", wait, desiredLast, last)
|
||||
// }
|
||||
// if last.After(desiredLast) {
|
||||
// t.Fatalf("timer advanced too far. should only have gone up to %s, but it went up to %s", desiredLast, last)
|
||||
// }
|
||||
// return last
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func assertNoAdvance(ticker *Ticker, desiredLast time.Time, wait time.Duration, t *testing.T) {
|
||||
// for {
|
||||
// select {
|
||||
// case tick := <-ticker.C:
|
||||
// t.Fatalf("timer should have stayed at %s, instead it advanced to %s", desiredLast, tick)
|
||||
// case <-time.NewTimer(wait).C:
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func TestTickerRetro1Hour(t *testing.T) {
|
||||
// offset := time.Duration(10) * time.Second
|
||||
// last := time.Unix(0, 0)
|
||||
// mock := clock.NewMock()
|
||||
// mock.Add(time.Duration(1) * time.Hour)
|
||||
// desiredLast := mock.Now().Add(-offset)
|
||||
// ticker := NewTicker(last, offset, mock)
|
||||
//
|
||||
// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func TestAdvanceWithUpdateOffset(t *testing.T) {
|
||||
// offset := time.Duration(10) * time.Second
|
||||
// last := time.Unix(0, 0)
|
||||
// mock := clock.NewMock()
|
||||
// mock.Add(time.Duration(1) * time.Hour)
|
||||
// desiredLast := mock.Now().Add(-offset)
|
||||
// ticker := NewTicker(last, offset, mock)
|
||||
//
|
||||
// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(10)*time.Millisecond, t)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
//
|
||||
// // lowering offset should see a few more ticks
|
||||
// offset = time.Duration(5) * time.Second
|
||||
// ticker.updateOffset(offset)
|
||||
// desiredLast = mock.Now().Add(-offset)
|
||||
// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(9)*time.Millisecond, t)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
//
|
||||
// // advancing clock should see even more ticks
|
||||
// mock.Add(time.Duration(1) * time.Hour)
|
||||
// desiredLast = mock.Now().Add(-offset)
|
||||
// last = assertAdvanceUntil(ticker, last, desiredLast, offset, time.Duration(8)*time.Millisecond, t)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func getCase(lastSeconds, offsetSeconds int) (time.Time, time.Duration) {
|
||||
// last := time.Unix(int64(lastSeconds), 0)
|
||||
// offset := time.Duration(offsetSeconds) * time.Second
|
||||
// return last, offset
|
||||
// }
|
||||
//
|
||||
// func TestTickerNoAdvance(t *testing.T) {
|
||||
//
|
||||
// // it's 00:01:00 now. what are some cases where we don't want the ticker to advance?
|
||||
// mock := clock.NewMock()
|
||||
// mock.Add(time.Duration(60) * time.Second)
|
||||
//
|
||||
// type Case struct {
|
||||
// last int
|
||||
// offset int
|
||||
// }
|
||||
//
|
||||
// // note that some cases add up to now, others go into the future
|
||||
// cases := []Case{
|
||||
// {50, 10},
|
||||
// {50, 30},
|
||||
// {59, 1},
|
||||
// {59, 10},
|
||||
// {59, 30},
|
||||
// {60, 1},
|
||||
// {60, 10},
|
||||
// {60, 30},
|
||||
// {90, 1},
|
||||
// {90, 10},
|
||||
// {90, 30},
|
||||
// }
|
||||
// for _, c := range cases {
|
||||
// last, offset := getCase(c.last, c.offset)
|
||||
// ticker := NewTicker(last, offset, mock)
|
||||
// assertNoAdvance(ticker, last, time.Duration(500)*time.Millisecond, t)
|
||||
// }
|
||||
// }
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTicker(t *testing.T) {
|
||||
readChanOrFail := func(t *testing.T, ctx context.Context, c chan time.Time) time.Time {
|
||||
t.Helper()
|
||||
select {
|
||||
case tick := <-c:
|
||||
return tick
|
||||
case <-ctx.Done():
|
||||
require.Failf(t, fmt.Sprintf("%v", ctx.Err()), "timeout reading the channel")
|
||||
default:
|
||||
require.Failf(t, "channel is empty but it should have a tick", "")
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
t.Run("should not drop ticks", func(t *testing.T) {
|
||||
clk := clock.NewMock()
|
||||
intervalSec := rand.Int63n(100) + 10
|
||||
interval := time.Duration(intervalSec) * time.Second
|
||||
last := clk.Now()
|
||||
ticker := NewTicker(last, 0, clk, intervalSec)
|
||||
|
||||
ticks := rand.Intn(9) + 1
|
||||
jitter := rand.Int63n(int64(interval) - 1)
|
||||
|
||||
clk.Add(time.Duration(ticks)*interval + time.Duration(jitter))
|
||||
|
||||
w := sync.WaitGroup{}
|
||||
w.Add(1)
|
||||
regTicks := make([]time.Time, 0, ticks)
|
||||
go func() {
|
||||
for timestamp := range ticker.C {
|
||||
regTicks = append(regTicks, timestamp)
|
||||
if len(regTicks) == ticks {
|
||||
w.Done()
|
||||
}
|
||||
}
|
||||
}()
|
||||
w.Wait()
|
||||
|
||||
require.Len(t, regTicks, ticks)
|
||||
|
||||
t.Run("ticks should monotonically increase", func(t *testing.T) {
|
||||
for i := 1; i < len(regTicks); i++ {
|
||||
previous := regTicks[i-1]
|
||||
current := regTicks[i]
|
||||
require.Equal(t, interval, current.Sub(previous))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("should not put anything to channel until it's time", func(t *testing.T) {
|
||||
clk := clock.NewMock()
|
||||
intervalSec := rand.Int63n(9) + 1
|
||||
interval := time.Duration(intervalSec) * time.Second
|
||||
last := clk.Now()
|
||||
ticker := NewTicker(last, 0, clk, intervalSec)
|
||||
expectedTick := clk.Now().Add(interval)
|
||||
for {
|
||||
require.Empty(t, ticker.C)
|
||||
clk.Add(time.Duration(rand.Int31n(500)+100) * time.Millisecond)
|
||||
if clk.Now().After(expectedTick) {
|
||||
break
|
||||
}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
t.Cleanup(func() {
|
||||
cancel()
|
||||
})
|
||||
actual := readChanOrFail(t, ctx, ticker.C)
|
||||
require.Equal(t, expectedTick, actual)
|
||||
})
|
||||
|
||||
t.Run("should put the tick in the channel immediately if it is behind", func(t *testing.T) {
|
||||
clk := clock.NewMock()
|
||||
intervalSec := rand.Int63n(9) + 1
|
||||
interval := time.Duration(intervalSec) * time.Second
|
||||
last := clk.Now()
|
||||
ticker := NewTicker(last, 0, clk, intervalSec)
|
||||
|
||||
// We can expect the first tick to be at a consistent interval. Take a snapshot of the clock now, before we advance it.
|
||||
expectedTick := clk.Now().Add(interval)
|
||||
|
||||
require.Empty(t, ticker.C)
|
||||
|
||||
clk.Add(interval) // advance the clock by the interval to make the ticker tick the first time.
|
||||
clk.Add(interval) // advance the clock by the interval to make the ticker tick the second time.
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
t.Cleanup(func() {
|
||||
cancel()
|
||||
})
|
||||
|
||||
// Irregardless of wall time, the first tick should be initial clock + interval.
|
||||
actual1 := readChanOrFail(t, ctx, ticker.C)
|
||||
require.Equal(t, expectedTick, actual1)
|
||||
|
||||
var actual2 time.Time
|
||||
require.Eventually(t, func() bool {
|
||||
actual2 = readChanOrFail(t, ctx, ticker.C)
|
||||
return true
|
||||
}, time.Second, 10*time.Millisecond)
|
||||
|
||||
// Similarly, the second tick should be last tick + interval irregardless of wall time.
|
||||
require.Equal(t, expectedTick.Add(interval), actual2)
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user