155 lines
4.4 KiB
Go
155 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// A frame lands in the bucket its receive stamp falls in, whenever the worker
|
|
// got round to draining it.
|
|
func TestRxObserveBucketsByStamp(t *testing.T) {
|
|
var s rxStats
|
|
run := rateRun{stats: &s}
|
|
run.add(3*int64(time.Millisecond), 100)
|
|
run.add(5*int64(time.Millisecond), 200)
|
|
run.add(rateBucketNs+int64(time.Millisecond), 300)
|
|
run.flush()
|
|
|
|
if f, b := s.bucket(0); f != 2 || b != 300 {
|
|
t.Errorf("epoch 0 = %d frames, %d bytes; want 2, 300", f, b)
|
|
}
|
|
if f, b := s.bucket(1); f != 1 || b != 300 {
|
|
t.Errorf("epoch 1 = %d frames, %d bytes; want 1, 300", f, b)
|
|
}
|
|
if got := s.newest.Load(); got != 1 {
|
|
t.Errorf("newest = %d, want 1", got)
|
|
}
|
|
}
|
|
|
|
// A slot coming round again belongs to its new epoch, and the epoch it replaced
|
|
// reports nothing rather than the stale counts.
|
|
func TestRxBucketWraps(t *testing.T) {
|
|
var s rxStats
|
|
run := rateRun{stats: &s}
|
|
run.add(1, 100)
|
|
run.add(rateBuckets*rateBucketNs+1, 200)
|
|
run.flush()
|
|
|
|
if f, b := s.bucket(0); f != 0 || b != 0 {
|
|
t.Errorf("evicted epoch = %d frames, %d bytes; want 0, 0", f, b)
|
|
}
|
|
if f, b := s.bucket(rateBuckets); f != 1 || b != 200 {
|
|
t.Errorf("new epoch = %d frames, %d bytes; want 1, 200", f, b)
|
|
}
|
|
}
|
|
|
|
// The newest epoch may still be filling, so the rate comes from the one before.
|
|
func TestDirectionRateReadsOneBucketBack(t *testing.T) {
|
|
d := &direction{rxStats: []*rxStats{{}, {}}}
|
|
r0 := rateRun{stats: d.rxStats[0]}
|
|
r1 := rateRun{stats: d.rxStats[1]}
|
|
r0.add(rateBucketNs+1, 500)
|
|
r1.add(rateBucketNs+2, 700)
|
|
r0.add(2*rateBucketNs+1, 900)
|
|
r0.flush()
|
|
r1.flush()
|
|
|
|
d.readRateBucket(time.Now())
|
|
if d.rateFrames != 2 || d.rateBytes != 1200 {
|
|
t.Errorf("rate bucket = %d frames, %d bytes; want the completed epoch, 2 and 1200",
|
|
d.rateFrames, d.rateBytes)
|
|
}
|
|
}
|
|
|
|
// A stamp only advances when a frame arrives, so an epoch that stops moving is
|
|
// a quiet wire and must not keep reporting the last bucket.
|
|
func TestDirectionRateGoesStale(t *testing.T) {
|
|
d := &direction{rxStats: []*rxStats{{}}}
|
|
run := rateRun{stats: d.rxStats[0]}
|
|
run.add(rateBucketNs+1, 500)
|
|
run.add(2*rateBucketNs+1, 900)
|
|
run.flush()
|
|
|
|
now := time.Now()
|
|
d.readRateBucket(now)
|
|
if d.rateFrames == 0 {
|
|
t.Fatal("expected a rate while the epoch was still moving")
|
|
}
|
|
d.readRateBucket(now.Add(2 * rateStale))
|
|
if d.rateFrames != 0 || d.rateBytes != 0 {
|
|
t.Errorf("stale rate = %d frames, %d bytes; want 0, 0", d.rateFrames, d.rateBytes)
|
|
}
|
|
}
|
|
|
|
// Indexed oldest first, so the error window still spans the whole ring once it
|
|
// has wrapped.
|
|
func TestRateWindowIndexesOldestFirst(t *testing.T) {
|
|
w := newRateWindow(3)
|
|
for i := 1; i <= 5; i++ {
|
|
w.push(counterSet{s: sample{rxFrames: uint64(i)}})
|
|
}
|
|
if got := w.count(); got != 3 {
|
|
t.Fatalf("count = %d, want 3", got)
|
|
}
|
|
if got := w.at(0).s.rxFrames; got != 3 {
|
|
t.Errorf("oldest = %d, want 3", got)
|
|
}
|
|
if got := w.at(2).s.rxFrames; got != 5 {
|
|
t.Errorf("newest = %d, want 5", got)
|
|
}
|
|
}
|
|
|
|
// Which counter feeds which bucket is the whole taxonomy the panel reports, so
|
|
// it is pinned here rather than left to whoever reads errsBetween next.
|
|
func TestErrsBetweenBuckets(t *testing.T) {
|
|
n := counterSet{
|
|
s: sample{
|
|
lost: 1, late: 7,
|
|
crcErr: 2, badMagic: 3, badLen: 4, badHdr: 10,
|
|
txErrs: 6, rxErrs: 5,
|
|
},
|
|
drops: 9,
|
|
nic: 8,
|
|
}
|
|
got := errsBetween(counterSet{}, n)
|
|
want := errs{lost: 1, corrupt: 2 + 3 + 4 + 10, link: 8 + 5, internal: 9 + 7 + 6}
|
|
if got != want {
|
|
t.Errorf("errsBetween = %+v, want %+v", got, want)
|
|
}
|
|
if got.total() != 55 {
|
|
t.Errorf("total = %d, want 55", got.total())
|
|
}
|
|
}
|
|
|
|
// A reset re-bases from a fresh capture while the ring still holds buckets from
|
|
// just before it, so the newest bucket must not be left behind the new origin.
|
|
func TestResetDoesNotUnderflowTotals(t *testing.T) {
|
|
d := &direction{
|
|
win: newRateWindow(8),
|
|
cable: newCableStats(),
|
|
rxStats: []*rxStats{{}},
|
|
}
|
|
|
|
d.rxStats[0].frames.Store(100)
|
|
d.rxStats[0].bytes.Store(6400)
|
|
d.sample()
|
|
|
|
d.rxStats[0].frames.Store(150)
|
|
d.rxStats[0].bytes.Store(9600)
|
|
d.reset()
|
|
|
|
v := d.displayView()
|
|
if v.rxFrames != 0 {
|
|
t.Errorf("rxFrames = %d, want 0", v.rxFrames)
|
|
}
|
|
if v.rxBytes != 0 {
|
|
t.Errorf("rxBytes = %d, want 0", v.rxBytes)
|
|
}
|
|
|
|
// The rolling window and the rates are about now rather than since the
|
|
// reset, so the buckets from before it have to survive.
|
|
if got := d.win.count(); got < 2 {
|
|
t.Errorf("ring holds %d buckets after reset, want the pre-reset history kept", got)
|
|
}
|
|
}
|