252 lines
5.4 KiB
Go
252 lines
5.4 KiB
Go
|
package agent
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/benbjohnson/clock"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
var format = "2006-01-02T15:04:05.999Z07:00"
|
||
|
|
||
|
func TestAlignedTicker(t *testing.T) {
|
||
|
interval := 10 * time.Second
|
||
|
jitter := 0 * time.Second
|
||
|
|
||
|
clock := clock.NewMock()
|
||
|
since := clock.Now()
|
||
|
until := since.Add(60 * time.Second)
|
||
|
|
||
|
ticker := newAlignedTicker(since, interval, jitter, clock)
|
||
|
|
||
|
expected := []time.Time{
|
||
|
time.Unix(10, 0).UTC(),
|
||
|
time.Unix(20, 0).UTC(),
|
||
|
time.Unix(30, 0).UTC(),
|
||
|
time.Unix(40, 0).UTC(),
|
||
|
time.Unix(50, 0).UTC(),
|
||
|
time.Unix(60, 0).UTC(),
|
||
|
}
|
||
|
|
||
|
actual := []time.Time{}
|
||
|
for !clock.Now().After(until) {
|
||
|
select {
|
||
|
case tm := <-ticker.Elapsed():
|
||
|
actual = append(actual, tm.UTC())
|
||
|
default:
|
||
|
}
|
||
|
clock.Add(10 * time.Second)
|
||
|
}
|
||
|
|
||
|
require.Equal(t, expected, actual)
|
||
|
}
|
||
|
|
||
|
func TestAlignedTickerJitter(t *testing.T) {
|
||
|
interval := 10 * time.Second
|
||
|
jitter := 5 * time.Second
|
||
|
|
||
|
clock := clock.NewMock()
|
||
|
since := clock.Now()
|
||
|
until := since.Add(60 * time.Second)
|
||
|
|
||
|
ticker := newAlignedTicker(since, interval, jitter, clock)
|
||
|
|
||
|
last := since
|
||
|
for !clock.Now().After(until) {
|
||
|
select {
|
||
|
case tm := <-ticker.Elapsed():
|
||
|
require.True(t, tm.Sub(last) <= 15*time.Second)
|
||
|
require.True(t, tm.Sub(last) >= 5*time.Second)
|
||
|
last = last.Add(interval)
|
||
|
default:
|
||
|
}
|
||
|
clock.Add(5 * time.Second)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestAlignedTickerMissedTick(t *testing.T) {
|
||
|
interval := 10 * time.Second
|
||
|
jitter := 0 * time.Second
|
||
|
|
||
|
clock := clock.NewMock()
|
||
|
since := clock.Now()
|
||
|
|
||
|
ticker := newAlignedTicker(since, interval, jitter, clock)
|
||
|
|
||
|
clock.Add(25 * time.Second)
|
||
|
tm := <-ticker.Elapsed()
|
||
|
require.Equal(t, time.Unix(10, 0).UTC(), tm.UTC())
|
||
|
clock.Add(5 * time.Second)
|
||
|
tm = <-ticker.Elapsed()
|
||
|
require.Equal(t, time.Unix(30, 0).UTC(), tm.UTC())
|
||
|
}
|
||
|
|
||
|
func TestUnalignedTicker(t *testing.T) {
|
||
|
interval := 10 * time.Second
|
||
|
jitter := 0 * time.Second
|
||
|
|
||
|
clock := clock.NewMock()
|
||
|
clock.Add(1 * time.Second)
|
||
|
since := clock.Now()
|
||
|
until := since.Add(60 * time.Second)
|
||
|
|
||
|
ticker := newUnalignedTicker(interval, jitter, clock)
|
||
|
|
||
|
expected := []time.Time{
|
||
|
time.Unix(1, 0).UTC(),
|
||
|
time.Unix(11, 0).UTC(),
|
||
|
time.Unix(21, 0).UTC(),
|
||
|
time.Unix(31, 0).UTC(),
|
||
|
time.Unix(41, 0).UTC(),
|
||
|
time.Unix(51, 0).UTC(),
|
||
|
time.Unix(61, 0).UTC(),
|
||
|
}
|
||
|
|
||
|
actual := []time.Time{}
|
||
|
for !clock.Now().After(until) {
|
||
|
select {
|
||
|
case tm := <-ticker.Elapsed():
|
||
|
actual = append(actual, tm.UTC())
|
||
|
default:
|
||
|
}
|
||
|
clock.Add(10 * time.Second)
|
||
|
}
|
||
|
|
||
|
require.Equal(t, expected, actual)
|
||
|
}
|
||
|
|
||
|
func TestRollingTicker(t *testing.T) {
|
||
|
interval := 10 * time.Second
|
||
|
jitter := 0 * time.Second
|
||
|
|
||
|
clock := clock.NewMock()
|
||
|
clock.Add(1 * time.Second)
|
||
|
since := clock.Now()
|
||
|
until := since.Add(60 * time.Second)
|
||
|
|
||
|
ticker := newUnalignedTicker(interval, jitter, clock)
|
||
|
|
||
|
expected := []time.Time{
|
||
|
time.Unix(1, 0).UTC(),
|
||
|
time.Unix(11, 0).UTC(),
|
||
|
time.Unix(21, 0).UTC(),
|
||
|
time.Unix(31, 0).UTC(),
|
||
|
time.Unix(41, 0).UTC(),
|
||
|
time.Unix(51, 0).UTC(),
|
||
|
time.Unix(61, 0).UTC(),
|
||
|
}
|
||
|
|
||
|
actual := []time.Time{}
|
||
|
for !clock.Now().After(until) {
|
||
|
select {
|
||
|
case tm := <-ticker.Elapsed():
|
||
|
actual = append(actual, tm.UTC())
|
||
|
default:
|
||
|
}
|
||
|
clock.Add(10 * time.Second)
|
||
|
}
|
||
|
|
||
|
require.Equal(t, expected, actual)
|
||
|
}
|
||
|
|
||
|
// Simulates running the Ticker for an hour and displays stats about the
|
||
|
// operation.
|
||
|
func TestAlignedTickerDistribution(t *testing.T) {
|
||
|
if testing.Short() {
|
||
|
t.Skip("skipping test in short mode.")
|
||
|
}
|
||
|
|
||
|
interval := 10 * time.Second
|
||
|
jitter := 5 * time.Second
|
||
|
|
||
|
clock := clock.NewMock()
|
||
|
since := clock.Now()
|
||
|
|
||
|
ticker := newAlignedTicker(since, interval, jitter, clock)
|
||
|
dist := simulatedDist(ticker, clock)
|
||
|
printDist(dist)
|
||
|
require.True(t, 350 < dist.Count)
|
||
|
require.True(t, 9 < dist.Mean() && dist.Mean() < 11)
|
||
|
}
|
||
|
|
||
|
// Simulates running the Ticker for an hour and displays stats about the
|
||
|
// operation.
|
||
|
func TestUnalignedTickerDistribution(t *testing.T) {
|
||
|
if testing.Short() {
|
||
|
t.Skip("skipping test in short mode.")
|
||
|
}
|
||
|
|
||
|
interval := 10 * time.Second
|
||
|
jitter := 5 * time.Second
|
||
|
|
||
|
clock := clock.NewMock()
|
||
|
|
||
|
ticker := newUnalignedTicker(interval, jitter, clock)
|
||
|
dist := simulatedDist(ticker, clock)
|
||
|
printDist(dist)
|
||
|
require.True(t, 350 < dist.Count)
|
||
|
require.True(t, 9 < dist.Mean() && dist.Mean() < 11)
|
||
|
}
|
||
|
|
||
|
// Simulates running the Ticker for an hour and displays stats about the
|
||
|
// operation.
|
||
|
func TestRollingTickerDistribution(t *testing.T) {
|
||
|
if testing.Short() {
|
||
|
t.Skip("skipping test in short mode.")
|
||
|
}
|
||
|
|
||
|
interval := 10 * time.Second
|
||
|
jitter := 5 * time.Second
|
||
|
|
||
|
clock := clock.NewMock()
|
||
|
|
||
|
ticker := newRollingTicker(interval, jitter, clock)
|
||
|
dist := simulatedDist(ticker, clock)
|
||
|
printDist(dist)
|
||
|
require.True(t, 275 < dist.Count)
|
||
|
require.True(t, 12 < dist.Mean() && 13 > dist.Mean())
|
||
|
}
|
||
|
|
||
|
type Distribution struct {
|
||
|
Buckets [60]int
|
||
|
Count int
|
||
|
Waittime float64
|
||
|
}
|
||
|
|
||
|
func (d *Distribution) Mean() float64 {
|
||
|
return d.Waittime / float64(d.Count)
|
||
|
}
|
||
|
|
||
|
func printDist(dist Distribution) {
|
||
|
for i, count := range dist.Buckets {
|
||
|
fmt.Printf("%2d %s\n", i, strings.Repeat("x", count))
|
||
|
}
|
||
|
fmt.Printf("Average interval: %f\n", dist.Mean())
|
||
|
fmt.Printf("Count: %d\n", dist.Count)
|
||
|
}
|
||
|
|
||
|
func simulatedDist(ticker Ticker, clock *clock.Mock) Distribution {
|
||
|
since := clock.Now()
|
||
|
until := since.Add(1 * time.Hour)
|
||
|
|
||
|
var dist Distribution
|
||
|
|
||
|
last := clock.Now()
|
||
|
for !clock.Now().After(until) {
|
||
|
select {
|
||
|
case tm := <-ticker.Elapsed():
|
||
|
dist.Buckets[tm.Second()] += 1
|
||
|
dist.Count++
|
||
|
dist.Waittime += tm.Sub(last).Seconds()
|
||
|
last = tm
|
||
|
default:
|
||
|
clock.Add(1 * time.Second)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return dist
|
||
|
}
|