package main import ( "testing" "time" ) // sent is the sender's frontier: sequence numbers at or above it were never put // on the wire. func newWindow(sent uint64) *lossWindow { tx := &txStats{} tx.sent.Store(sent) w := newLossWindows([]*txStats{tx}) return &w[0] } func TestLossContiguousLosesNothing(t *testing.T) { w := newWindow(10000) for seq := uint64(0); seq < 10000; seq++ { w.observe(seq) } if got := w.lost.Load(); got != 0 { t.Errorf("lost = %d, want 0", got) } if got := w.late.Load(); got != 0 { t.Errorf("late = %d, want 0", got) } } // An arrival is the judgment: the gap between it and the previous arrival is // lost the moment it lands, not when some window slides. func TestLossGapChargesOnArrival(t *testing.T) { w := newWindow(10000) for seq := uint64(0); seq < 100; seq++ { w.observe(seq) } w.observe(105) if got := w.lost.Load(); got != 5 { t.Errorf("lost = %d, want 5 the moment the gap's far edge arrived", got) } } // No tolerance of reordering: the newer frame writes the span off, and the // older one arriving after it is late, not a repair. func TestLossReorderChargesAndCountsLate(t *testing.T) { w := newWindow(10000) for seq := uint64(0); seq < 6; seq++ { w.observe(seq) } w.observe(7) if got := w.lost.Load(); got != 1 { t.Errorf("lost = %d after the skip, want 1", got) } w.observe(6) if got := w.late.Load(); got != 1 { t.Errorf("late = %d, want 1", got) } if got := w.lost.Load(); got != 1 { t.Errorf("lost = %d after the late arrival, want still 1", got) } } // A silent wire is charged by the judge within one deadline: everything the // frontier covered a deadline ago and nothing has delivered since is lost. func TestLossJudgeChargesSilentWire(t *testing.T) { w := newWindow(1000) t0 := time.Now() w.judge(t0) w.judge(t0.Add(lossDeadline / 2)) if got := w.lost.Load(); got != 0 { t.Errorf("lost = %d before the deadline, want 0", got) } w.judge(t0.Add(lossDeadline)) if got := w.lost.Load(); got != 1000 { t.Errorf("lost = %d after the deadline, want the whole silent frontier, 1000", got) } } // Arrivals move next past the old frontier, so the judge has nothing to add. func TestLossJudgeSkipsDelivered(t *testing.T) { w := newWindow(1000) t0 := time.Now() w.judge(t0) for seq := uint64(0); seq < 1000; seq++ { w.observe(seq) } w.judge(t0.Add(lossDeadline)) if got := w.lost.Load(); got != 0 { t.Errorf("lost = %d, want 0", got) } } // A frame the judge already wrote off arriving afterwards is late, and the // charge is not repaired. func TestLossJudgedFrameArrivingIsLate(t *testing.T) { w := newWindow(1000) t0 := time.Now() w.judge(t0) w.judge(t0.Add(lossDeadline)) w.observe(500) if got := w.late.Load(); got != 1 { t.Errorf("late = %d, want 1", got) } if got := w.lost.Load(); got != 1000 { t.Errorf("lost = %d, want unchanged 1000", got) } }