Files
Unlucky-Launcher/crates/core/tests/download/backoff_test.rs
T
AskaEth 8a8e5ca0c0
CI / fmt (rustfmt --check) (push) Waiting to run
CI / clippy (ubuntu-latest) (push) Waiting to run
CI / clippy (windows-latest) (push) Waiting to run
CI / test (ubuntu-latest) (push) Waiting to run
CI / test (windows-latest) (push) Waiting to run
CI / build release (ubuntu-latest) (push) Waiting to run
CI / build release (windows-latest) (push) Waiting to run
CI / runtime smoke (ubuntu x64, xvfb) (push) Waiting to run
CI / runtime smoke (macos arm64, probe) (push) Skipped
Guards / core 零 UI 守门 (push) Waiting to run
Guards / presenter 零 UI 守门 (push) Waiting to run
Guards / design 零第三方依赖守门 (push) Waiting to run
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
Unlucky Launcher v0.2.0
首个 iced + Material 3 Expressive 版本的源码快照。
完整历史与日常提交在 dev 分支;main 只放阶段性快照。
2026-11-05 14:22:11 +08:00

69 lines
2.2 KiB
Rust

//! 指数退避与抖动的单测(纯函数,无网络)。
use std::time::Duration;
use unlucky_core::download::backoff::{BackoffPolicy, Jitter};
#[test]
fn exponential_growth_doubles_and_caps() {
let policy = BackoffPolicy::default();
assert_eq!(policy.delay(0), Duration::from_millis(1_000));
assert_eq!(policy.delay(1), Duration::from_millis(2_000));
assert_eq!(policy.delay(2), Duration::from_millis(4_000));
assert_eq!(policy.delay(3), Duration::from_millis(8_000));
// 上限封顶,不会涨到分钟级。
assert_eq!(policy.delay(10), Duration::from_millis(30_000));
assert_eq!(policy.delay(u32::MAX), Duration::from_millis(30_000));
}
#[test]
fn no_retry_policy_waits_nothing() {
let policy = BackoffPolicy::none();
assert_eq!(policy.delay(0), Duration::ZERO);
assert_eq!(policy.max_attempts, 1);
assert_eq!(policy.delay_jittered(0, 0.5), Duration::ZERO);
}
#[test]
fn jitter_stays_within_ratio_bounds() {
let policy = BackoffPolicy::default();
// base 1000ms ± 30% → [700, 1300]
let low = policy.delay_jittered(0, 0.0);
let high = policy.delay_jittered(0, 1.0);
assert_eq!(low, Duration::from_millis(700));
assert_eq!(high, Duration::from_millis(1_300));
// 抖动永不超过 max_ms。
for rng in [0.0, 0.25, 0.5, 0.75, 1.0] {
assert!(policy.delay_jittered(9, rng) <= Duration::from_millis(30_000));
}
}
#[test]
fn jitter_without_ratio_is_deterministic() {
let policy = BackoffPolicy {
jitter_ratio: 0.0,
..BackoffPolicy::default()
};
assert_eq!(
policy.delay_jittered(2, 0.0),
policy.delay_jittered(2, 0.99)
);
}
#[test]
fn jitter_source_is_reproducible_and_in_range() {
let mut first = Jitter::new(42);
let mut second = Jitter::new(42);
for _ in 0..32 {
let a = first.next_fraction();
let b = second.next_fraction();
assert_eq!(a, b, "同种子的抖动序列必须一致");
assert!((0.0..1.0).contains(&a));
}
let mut other = Jitter::new(7);
assert_ne!(Jitter::new(1).next_u64(), other.next_u64());
// 种子 0 会被替换成固定非零种子,但仍然可用。
assert!((0.0..1.0).contains(&Jitter::new(0).next_fraction()));
}