blob: 5800148602eacb9703c42782c01f604b1eef93fc [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package counter_test
import (
"fmt"
"math"
"math/rand"
"sync"
"testing"
"time"
"v.io/x/ref/lib/stats/counter"
)
var trackerTests = []struct {
after time.Duration
push int64 // 0 = none, -1 = reset
mins, maxs []int64 // min, 10min, hour, all time
}{
{ // T0
after: 0,
push: 0,
mins: []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64},
maxs: []int64{math.MinInt64, math.MinInt64, math.MinInt64, math.MinInt64},
},
{ // T1
after: 1 * time.Second,
push: 5,
mins: []int64{5, 5, 5, 5},
maxs: []int64{5, 5, 5, 5},
},
{ // T2
after: 1 * time.Second,
push: 10,
mins: []int64{5, 5, 5, 5},
maxs: []int64{10, 10, 10, 10},
},
{ // T3
after: 1 * time.Second,
push: 1,
mins: []int64{1, 1, 1, 1},
maxs: []int64{10, 10, 10, 10},
},
{ // T4
after: 1 * time.Minute,
push: 0,
mins: []int64{math.MaxInt64, 1, 1, 1},
maxs: []int64{math.MinInt64, 10, 10, 10},
},
{ // T5
after: 10 * time.Minute,
push: 0,
mins: []int64{math.MaxInt64, math.MaxInt64, 1, 1},
maxs: []int64{math.MinInt64, math.MinInt64, 10, 10},
},
{ // T6
after: 1 * time.Hour,
push: 0,
mins: []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, 1},
maxs: []int64{math.MinInt64, math.MinInt64, math.MinInt64, 10},
},
{ // T7
after: 1 * time.Second,
push: 5,
mins: []int64{5, 5, 5, 1},
maxs: []int64{5, 5, 5, 10},
},
{ // T8
after: 1 * time.Minute,
push: 20,
mins: []int64{20, 5, 5, 1},
maxs: []int64{20, 20, 20, 20},
},
{ // T9
after: 10 * time.Minute,
push: 15,
mins: []int64{15, 15, 5, 1},
maxs: []int64{15, 15, 20, 20},
},
{ // T10
after: 1 * time.Hour,
push: 10,
mins: []int64{10, 10, 10, 1},
maxs: []int64{10, 10, 10, 20},
},
{ // T11
after: 1 * time.Second,
push: -1,
mins: []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64},
maxs: []int64{math.MinInt64, math.MinInt64, math.MinInt64, math.MinInt64},
},
{ // T12
after: 1 * time.Second,
push: 5,
mins: []int64{5, 5, 5, 5},
maxs: []int64{5, 5, 5, 5},
},
}
func TestTracker(t *testing.T) {
now := time.Unix(1, 0)
counter.TimeNow = func() time.Time { return now }
tracker := counter.NewTracker()
for i, tt := range trackerTests {
now = now.Add(tt.after)
name := fmt.Sprintf("[T%d] %s:", i, now.Format("15:04:05"))
if tt.push > 0 {
tracker.Push(tt.push)
t.Logf("%s pushed %d\n", name, tt.push)
} else if tt.push < 0 {
tracker.Reset()
t.Log(name, "reset")
} else {
t.Log(name, "none")
}
if expected, got := tt.mins[0], tracker.Min1m(); got != expected {
t.Errorf("%s Min1m returned %d, want %v", name, got, expected)
}
if expected, got := tt.maxs[0], tracker.Max1m(); got != expected {
t.Errorf("%s Max1m returned %d, want %v", name, got, expected)
}
if expected, got := tt.mins[1], tracker.Min10m(); got != expected {
t.Errorf("%s Min10m returned %d, want %v", name, got, expected)
}
if expected, got := tt.maxs[1], tracker.Max10m(); got != expected {
t.Errorf("%s Max10m returned %d, want %v", name, got, expected)
}
if expected, got := tt.mins[2], tracker.Min1h(); got != expected {
t.Errorf("%s Min1h returned %d, want %v", name, got, expected)
}
if expected, got := tt.maxs[2], tracker.Max1h(); got != expected {
t.Errorf("%s Max1h returned %d, want %v", name, got, expected)
}
if expected, got := tt.mins[3], tracker.Min(); got != expected {
t.Errorf("%s Min returned %d, want %v", name, got, expected)
}
if expected, got := tt.maxs[3], tracker.Max(); got != expected {
t.Errorf("%s Max returned %d, want %v", name, got, expected)
}
}
}
func min(a, b int64) int64 {
if a > b {
return b
}
return a
}
func max(a, b int64) int64 {
if a < b {
return b
}
return a
}
func TestTrackerConcurrent(t *testing.T) {
rand.Seed(time.Now().UnixNano())
const numGoRoutines = 100
const numPushPerGoRoutine = 100000
tracker := counter.NewTracker()
var mins, maxs [numGoRoutines]int64
var wg sync.WaitGroup
wg.Add(numGoRoutines)
for i := 0; i < numGoRoutines; i++ {
go func(i int) {
var localMin, localMax int64 = math.MaxInt64, math.MinInt64
for j := 0; j < numPushPerGoRoutine; j++ {
v := rand.Int63()
tracker.Push(v)
localMin, localMax = min(localMin, v), max(localMax, v)
}
mins[i], maxs[i] = localMin, localMax
wg.Done()
}(i)
}
wg.Wait()
var expectedMin, expectedMax int64 = math.MaxInt64, math.MinInt64
for _, v := range mins {
expectedMin = min(expectedMin, v)
}
for _, v := range maxs {
expectedMax = max(expectedMax, v)
}
if got := tracker.Min(); got != expectedMin {
t.Errorf("Min returned %d, want %v", got, expectedMin)
}
if got := tracker.Max(); got != expectedMax {
t.Errorf("Max returned %d, want %v", got, expectedMax)
}
}
func BenchmarkTrackerPush(b *testing.B) {
const numVals = 10000
vals := rand.Perm(numVals)
tracker := counter.NewTracker()
b.SetParallelism(100)
b.RunParallel(func(pb *testing.PB) {
for i := 0; pb.Next(); i++ {
tracker.Push(int64(vals[i%numVals]))
}
})
}