Skip to content

Commit

Permalink
test: use mock clocks for all tests (#25)
Browse files Browse the repository at this point in the history
This also:

1. Removes a race-test that likely isn't very useful anyways.
2. Removes the example "test".

But this is _much_ faster and will actually pass CI.
  • Loading branch information
Stebalien authored Nov 11, 2023
1 parent 69624ec commit eb03fd4
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 235 deletions.
222 changes: 95 additions & 127 deletions flow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,164 +9,132 @@ import (
)

func TestBasic(t *testing.T) {
if testing.Short() {
t.Skip("short testing requested")
m := new(Meter)
for i := 0; i < 300; i++ {
m.Mark(1000)
mockClock.Add(40 * time.Millisecond)
}
if rate := m.Snapshot().Rate; rate != 25000 {
t.Errorf("expected rate 25000, got %f", rate)
}
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
ticker := time.NewTicker(40 * time.Millisecond)
defer ticker.Stop()

m := new(Meter)
for i := 0; i < 300; i++ {
m.Mark(1000)
<-ticker.C
}
actual := m.Snapshot()
if !approxEq(actual.Rate, 25000, 1000) {
t.Errorf("expected rate 25000 (±1000), got %f", actual.Rate)
}

for i := 0; i < 200; i++ {
m.Mark(200)
<-ticker.C
}
for i := 0; i < 400; i++ {
m.Mark(200)
mockClock.Add(40 * time.Millisecond)
}

// Adjusts
actual = m.Snapshot()
if !approxEq(actual.Rate, 5000, 200) {
t.Errorf("expected rate 5000 (±200), got %f", actual.Rate)
}
if rate := m.Snapshot().Rate; !approxEq(rate, 5000, 1) {
t.Errorf("expected rate 5000, got %f", rate)
}

// Let it settle.
time.Sleep(2 * time.Second)
mockClock.Add(time.Second)

// get the right total
actual = m.Snapshot()
if actual.Total != 340000 {
t.Errorf("expected total %d, got %d", 340000, actual.Total)
}
}()
if total := m.Snapshot().Total; total != 380000 {
t.Errorf("expected total %d, got %d", 380000, total)
}
wg.Wait()
}

func TestShared(t *testing.T) {
if testing.Short() {
t.Skip("short testing requested")
}
var wg sync.WaitGroup
wg.Add(20 * 21)
for i := 0; i < 20; i++ {
m := new(Meter)
for j := 0; j < 20; j++ {
go func() {
defer wg.Done()
ticker := time.NewTicker(40 * time.Millisecond)
defer ticker.Stop()
for i := 0; i < 300; i++ {
m.Mark(50)
<-ticker.C
}

for i := 0; i < 200; i++ {
m.Mark(10)
<-ticker.C
}
}()
}
wg.Add(20)
m := new(Meter)
for j := 0; j < 20; j++ {
go func() {
defer wg.Done()
time.Sleep(40 * 300 * time.Millisecond)
actual := m.Snapshot()
if !approxEq(actual.Rate, 25000, 250) {
t.Errorf("expected rate 25000 (±250), got %f", actual.Rate)
for i := 0; i < 300; i++ {
m.Mark(50)
mockClock.Sleep(40 * time.Millisecond)
}

time.Sleep(40 * 200 * time.Millisecond)

// Adjusts
actual = m.Snapshot()
if !approxEq(actual.Rate, 5000, 50) {
t.Errorf("expected rate 5000 (±50), got %f", actual.Rate)
for i := 0; i < 300; i++ {
m.Mark(10)
mockClock.Sleep(40 * time.Millisecond)
}
}()
}

// Let it settle.
time.Sleep(2 * time.Second)
time.Sleep(time.Millisecond)
mockClock.Add(20 * 300 * time.Millisecond)
time.Sleep(time.Millisecond)
mockClock.Add(20 * 300 * time.Millisecond)
time.Sleep(time.Millisecond)

// get the right total
actual = m.Snapshot()
if actual.Total != 340000 {
t.Errorf("expected total %d, got %d", 340000, actual.Total)
}
}()
actual := m.Snapshot()
if !approxEq(actual.Rate, 25000, 1) {
t.Errorf("expected rate 25000, got %f", actual.Rate)
}

time.Sleep(time.Millisecond)
mockClock.Add(20 * 300 * time.Millisecond)
time.Sleep(time.Millisecond)
mockClock.Add(20 * 300 * time.Millisecond)
time.Sleep(time.Millisecond)

// Adjusts
actual = m.Snapshot()
if !approxEq(actual.Rate, 5000, 1) {
t.Errorf("expected rate 5000, got %f", actual.Rate)
}

// Let it settle.
time.Sleep(time.Millisecond)
mockClock.Add(time.Second)
time.Sleep(time.Millisecond)
mockClock.Add(time.Second)
time.Sleep(time.Millisecond)

// get the right total
actual = m.Snapshot()
if actual.Total != 360000 {
t.Errorf("expected total %d, got %d", 360000, actual.Total)
}
wg.Wait()
}

func TestUnregister(t *testing.T) {
if testing.Short() {
t.Skip("short testing requested")
m := new(Meter)

for i := 0; i < 40; i++ {
m.Mark(1)
mockClock.Add(100 * time.Millisecond)
}
var wg sync.WaitGroup
wg.Add(100 * 2)
for i := 0; i < 100; i++ {
m := new(Meter)
go func() {
defer wg.Done()
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for i := 0; i < 40; i++ {
m.Mark(1)
<-ticker.C
}

time.Sleep(62 * time.Second)
actual := m.Snapshot()
if actual.Rate != 10 {
t.Errorf("expected rate 10, got %f", actual.Rate)
}

for i := 0; i < 40; i++ {
m.Mark(2)
<-ticker.C
}
}()
go func() {
defer wg.Done()
time.Sleep(40 * 100 * time.Millisecond)
mockClock.Add(62 * time.Second)

actual := m.Snapshot()
if !approxEq(actual.Rate, 10, 1) {
t.Errorf("expected rate 10 (±1), got %f", actual.Rate)
}
if atomic.LoadUint64(&m.accumulator) != 0 {
t.Error("expected meter to be paused")
}

time.Sleep(60 * time.Second)
if atomic.LoadUint64(&m.accumulator) != 0 {
t.Error("expected meter to be paused")
}
actual = m.Snapshot()
if actual.Total != 40 {
t.Errorf("expected total 4000, got %d", actual.Total)
}

actual = m.Snapshot()
if actual.Total != 40 {
t.Errorf("expected total 4000, got %d", actual.Total)
}
time.Sleep(2*time.Second + 40*100*time.Millisecond)
for i := 0; i < 40; i++ {
m.Mark(2)
mockClock.Add(100 * time.Millisecond)
}

actual = m.Snapshot()
if !approxEq(actual.Rate, 20, 4) {
t.Errorf("expected rate 20 (±4), got %f", actual.Rate)
}
time.Sleep(2 * time.Second)
actual = m.Snapshot()
if actual.Total != 120 {
t.Errorf("expected total 120, got %d", actual.Total)
}
if atomic.LoadUint64(&m.accumulator) == 0 {
t.Error("expected meter to be active")
}
}()
actual = m.Snapshot()
if actual.Rate != 20 {
t.Errorf("expected rate 20, got %f", actual.Rate)
}

mockClock.Add(2 * time.Second)

actual = m.Snapshot()
if actual.Total != 120 {
t.Errorf("expected total 120, got %d", actual.Total)
}
wg.Wait()
if atomic.LoadUint64(&m.accumulator) == 0 {
t.Error("expected meter to be active")
}

}

func approxEq(a, b, err float64) bool {
Expand Down
2 changes: 1 addition & 1 deletion meter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Snapshot struct {
func NewMeter() *Meter {
return &Meter{
snapshot: Snapshot{
LastUpdate: time.Now(),
LastUpdate: cl.Now(),
},
}
}
Expand Down
52 changes: 2 additions & 50 deletions meter_test.go
Original file line number Diff line number Diff line change
@@ -1,42 +1,17 @@
package flow

import (
"fmt"
"math"
"sync"
"testing"
"time"
)

func ExampleMeter() {
meter := new(Meter)
t := time.NewTicker(100 * time.Millisecond)
for i := 0; i < 100; i++ {
<-t.C
meter.Mark(30)
}

// Get the current rate. This will be accurate *now* but not after we
// sleep (because we calculate it using EWMA).
rate := meter.Snapshot().Rate

// Sleep 2 seconds to allow the total to catch up. We snapshot every
// second so the total may not yet be accurate.
time.Sleep(2 * time.Second)

// Get the current total.
total := meter.Snapshot().Total

fmt.Printf("%d (%d/s)\n", total, roundTens(rate))
// Output: 3000 (300/s)
}

func TestResetMeter(t *testing.T) {
meter := new(Meter)

meter.Mark(30)

time.Sleep(2 * time.Second)
mockClock.Add(time.Millisecond)
mockClock.Add(1 * time.Second)

if total := meter.Snapshot().Total; total != 30 {
t.Errorf("total = %d; want 30", total)
Expand All @@ -48,26 +23,3 @@ func TestResetMeter(t *testing.T) {
t.Errorf("total = %d; want 0", total)
}
}

func TestMarkResetMeterMulti(t *testing.T) {
var wg sync.WaitGroup
wg.Add(2)

meter := new(Meter)
go func(meter *Meter) {
meter.Mark(30)
meter.Mark(30)
wg.Done()
}(meter)

go func(meter *Meter) {
meter.Reset()
wg.Done()
}(meter)

wg.Wait()
}

func roundTens(x float64) int64 {
return int64(math.Floor(x/10+0.5)) * 10
}
43 changes: 0 additions & 43 deletions mockclocktest/mock_clock_test.go

This file was deleted.

Loading

0 comments on commit eb03fd4

Please sign in to comment.