package qq_obv11 // 本文件只测退避策略本身(纯函数),不碰连接:重试循环的行为断言见 // client_retry_test.go。 import ( "testing" "time" ) // TestReconnectBackoffIsBounded 断言退避**有上限**:无论连续失败多少次, // 等待时长都不会超过 reconnectMaxDelay(对面的长期离线不能让等待无限增长)。 func TestReconnectBackoffIsBounded(t *testing.T) { t.Parallel() p := defaultReconnectPolicy() for failures := 0; failures <= 50; failures++ { got := p.delayFor(failures) if got <= 0 { t.Fatalf("delayFor(%d) = %v, want > 0", failures, got) } if got > reconnectMaxDelay { t.Fatalf("delayFor(%d) = %v, 超过上限 %v(退避无上限)", failures, got, reconnectMaxDelay) } } // 初值附近:抖动最多 ±reconnectJitterRatio。 base := p.delayFor(0) if base < time.Duration(float64(reconnectBaseDelay)*(1-reconnectJitterRatio)) || base > time.Duration(float64(reconnectBaseDelay)*(1+reconnectJitterRatio)) { t.Errorf("delayFor(0) = %v, 期望落在初值 %v ±%v 内", base, reconnectBaseDelay, reconnectJitterRatio) } } // TestReconnectBackoffGrowsThenCaps 断言退避按因子增长、到顶后稳住(把抖动关掉, // 取值才是确定的)。 func TestReconnectBackoffGrowsThenCaps(t *testing.T) { t.Parallel() p := defaultReconnectPolicy() p.jitterRatio = 0 if got := p.delayFor(0); got != reconnectBaseDelay { t.Errorf("delayFor(0) = %v, want %v", got, reconnectBaseDelay) } for _, tc := range []struct { failures int want time.Duration }{ {1, 4 * time.Second}, {2, 8 * time.Second}, {3, 16 * time.Second}, {4, 32 * time.Second}, {5, reconnectMaxDelay}, // 64s 被上限裁到 60s {20, reconnectMaxDelay}, // 远超上限仍是上限 } { if got := p.delayFor(tc.failures); got != tc.want { t.Errorf("delayFor(%d) = %v, want %v", tc.failures, got, tc.want) } } prev := time.Duration(0) for failures := 0; failures <= 10; failures++ { got := p.delayFor(failures) if got < prev { t.Fatalf("delayFor(%d) = %v < 前一次 %v(退避不单调)", failures, got, prev) } prev = got } } // TestReconnectBackoffJitters 断言抖动确实接了线:同一失败次数多次取值不全相等 // (多实例同时重连时打点才会错开)。 func TestReconnectBackoffJitters(t *testing.T) { t.Parallel() p := defaultReconnectPolicy() seen := map[time.Duration]struct{}{} for i := 0; i < 64; i++ { seen[p.delayFor(3)] = struct{}{} } if len(seen) < 2 { t.Errorf("delayFor(3) 反复取值只有 %d 种,抖动未生效", len(seen)) } }