diff --git a/flow_test.go b/flow_test.go index fb25828..edfdec4 100644 --- a/flow_test.go +++ b/flow_test.go @@ -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 { diff --git a/meter.go b/meter.go index b70593e..597efc4 100644 --- a/meter.go +++ b/meter.go @@ -20,7 +20,7 @@ type Snapshot struct { func NewMeter() *Meter { return &Meter{ snapshot: Snapshot{ - LastUpdate: time.Now(), + LastUpdate: cl.Now(), }, } } diff --git a/meter_test.go b/meter_test.go index 0f29a8e..8442ebd 100644 --- a/meter_test.go +++ b/meter_test.go @@ -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) @@ -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 -} diff --git a/mockclocktest/mock_clock_test.go b/mockclocktest/mock_clock_test.go deleted file mode 100644 index 9cff3d6..0000000 --- a/mockclocktest/mock_clock_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package mockclocktest - -import ( - "testing" - "time" - - "github.com/libp2p/go-flow-metrics" - - "github.com/benbjohnson/clock" -) - -var cl = clock.NewMock() - -func init() { - flow.SetClock(cl) -} - -func TestBasic(t *testing.T) { - m := new(flow.Meter) - for i := 0; i < 300; i++ { - m.Mark(1000) - cl.Add(40 * time.Millisecond) - } - if rate := m.Snapshot().Rate; rate != 25000 { - t.Errorf("expected rate 25000, got %f", rate) - } - - for i := 0; i < 200; i++ { - m.Mark(200) - cl.Add(40 * time.Millisecond) - } - - // Adjusts - if rate := m.Snapshot().Rate; rate != 5017.776503840969 { - t.Errorf("expected rate 5017.776503840969, got %f", rate) - } - - // Let it settle. - cl.Add(2 * time.Second) - if total := m.Snapshot().Total; total != 340000 { - t.Errorf("expected total 3400000, got %d", total) - } -} diff --git a/registry_test.go b/registry_test.go index f7976c9..ab20d84 100644 --- a/registry_test.go +++ b/registry_test.go @@ -11,11 +11,14 @@ func TestRegistry(t *testing.T) { m2 := r.Get("second") m1Update := m1.Snapshot().LastUpdate + mockClock.Add(5 * time.Second) m1.Mark(10) m2.Mark(30) - time.Sleep(2*time.Second + time.Millisecond) + mockClock.Add(1 * time.Second) + mockClock.Add(1 * time.Second) + mockClock.Add(1 * time.Millisecond) if total := r.Get("first").Snapshot().Total; total != 10 { t.Errorf("expected first total to be 10, got %d", total) @@ -24,8 +27,8 @@ func TestRegistry(t *testing.T) { t.Errorf("expected second total to be 30, got %d", total) } - if !m1.Snapshot().LastUpdate.After(m1Update) { - t.Error("expected the last update to have been updated") + if lu := m1.Snapshot().LastUpdate; !lu.After(m1Update) { + t.Errorf("expected the last update (%s) to have after (%s)", lu, m1Update) } expectedMeters := map[string]*Meter{ @@ -82,10 +85,11 @@ func TestRegistry(t *testing.T) { t.Errorf("missing meters: '%v'", expectedMeters) } - before := time.Now() + before := mockClock.Now() + mockClock.Add(time.Millisecond) m3.Mark(1) - time.Sleep(2 * time.Second) - after := time.Now() + mockClock.Add(2 * time.Second) + after := mockClock.Now() if len(r.FindIdle(before)) != 1 { t.Error("expected 1 idle timer") } @@ -107,7 +111,7 @@ func TestClearRegistry(t *testing.T) { m1.Mark(10) m2.Mark(30) - time.Sleep(2 * time.Second) + mockClock.Add(2 * time.Second) r.Clear() diff --git a/sweeper_test.go b/sweeper_test.go index 2ba9188..6e7da6d 100644 --- a/sweeper_test.go +++ b/sweeper_test.go @@ -3,14 +3,18 @@ package flow import ( "testing" "time" + + "github.com/benbjohnson/clock" ) +var mockClock = clock.NewMock() + +func init() { + SetClock(mockClock) +} + // regression test for libp2p/go-libp2p-core#65 func TestIdleInconsistency(t *testing.T) { - if testing.Short() { - t.Skip("short testing requested") - } - r := new(MeterRegistry) m1 := r.Get("first") m2 := r.Get("second") @@ -22,15 +26,15 @@ func TestIdleInconsistency(t *testing.T) { // make m1 and m3 go idle for i := 0; i < 30; i++ { - time.Sleep(time.Second) + mockClock.Add(time.Second) m2.Mark(1) } - time.Sleep(time.Second) + mockClock.Add(time.Second) // re-activate m3 m3.Mark(20) - time.Sleep(time.Second + time.Millisecond) + mockClock.Add(time.Second) // check the totals if total := r.Get("first").Snapshot().Total; total != 10 {