mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
扫描前自动探测网络环境(RTT、丢包率、fd limit),基于探测数据 推导 6 个关键参数,替代硬编码默认值: - Timeout: median_RTT + 4σ(覆盖 99.9% 正常连接) - ModuleThreadNum: target_concurrency / 30 - MaxRetries: ceil(log(0.01)/log(loss_rate))(全失败概率 <1%) - ICMPRate: 环境基准 × fd 系数 - PocNum: 跟随 ModuleThreadNum - DisablePing: 已有 ICMP 权限降级机制 线程池从单信号(资源耗尽率)升级为 AIMD + 慢启动: - 慢启动:target/4 起步,500ms 翻倍 - 稳态 AIMD:健康 +5%,拥塞 ×0.5 - 双信号:资源耗尽率 + RTT 趋势(双 EMA) 用户 -t 显式指定时作为 ceiling,探测仍调整其他参数。 测试:单元 + 边界 + 集成 + 真实网络,core 包 580+ 用例全通过。
131 lines
3.2 KiB
Go
131 lines
3.2 KiB
Go
package core
|
||
|
||
import (
|
||
"sync"
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// =============================================================================
|
||
// 单元测试:ScanMetrics 基本操作
|
||
// =============================================================================
|
||
|
||
func TestScanMetrics_Counters(t *testing.T) {
|
||
m := &ScanMetrics{}
|
||
|
||
m.RecordConnect(time.Millisecond)
|
||
m.RecordConnect(2 * time.Millisecond)
|
||
m.RecordRefused(500 * time.Microsecond)
|
||
m.RecordTimeout()
|
||
m.RecordExhausted()
|
||
|
||
if m.Total() != 5 {
|
||
t.Errorf("Total() = %d, want 5", m.Total())
|
||
}
|
||
|
||
snap := m.Snapshot()
|
||
if snap.Connects != 2 {
|
||
t.Errorf("Connects = %d, want 2", snap.Connects)
|
||
}
|
||
if snap.Refused != 1 {
|
||
t.Errorf("Refused = %d, want 1", snap.Refused)
|
||
}
|
||
if snap.Timeouts != 1 {
|
||
t.Errorf("Timeouts = %d, want 1", snap.Timeouts)
|
||
}
|
||
if snap.Exhausted != 1 {
|
||
t.Errorf("Exhausted = %d, want 1", snap.Exhausted)
|
||
}
|
||
}
|
||
|
||
// =============================================================================
|
||
// 单元测试:RTT EMA 收敛
|
||
// =============================================================================
|
||
|
||
func TestScanMetrics_RTT_EMA(t *testing.T) {
|
||
m := &ScanMetrics{}
|
||
|
||
// 喂入稳定的 10ms RTT
|
||
for i := 0; i < 100; i++ {
|
||
m.RecordConnect(10 * time.Millisecond)
|
||
}
|
||
|
||
fast := m.RTTFast()
|
||
if fast < 9*time.Millisecond || fast > 11*time.Millisecond {
|
||
t.Errorf("稳定 10ms 后 RTTFast = %v, 应该接近 10ms", fast)
|
||
}
|
||
|
||
ratio := m.RTTRatio()
|
||
if ratio < 0.9 || ratio > 1.1 {
|
||
t.Errorf("稳定状态 RTTRatio = %.2f, 应该接近 1.0", ratio)
|
||
}
|
||
}
|
||
|
||
func TestScanMetrics_RTT_Trend(t *testing.T) {
|
||
m := &ScanMetrics{}
|
||
|
||
// 先喂入 100 个 5ms 建立基线
|
||
for i := 0; i < 100; i++ {
|
||
m.RecordConnect(5 * time.Millisecond)
|
||
}
|
||
|
||
// 再喂入 50 个 50ms(RTT 突增 10 倍)
|
||
for i := 0; i < 50; i++ {
|
||
m.RecordConnect(50 * time.Millisecond)
|
||
}
|
||
|
||
ratio := m.RTTRatio()
|
||
// fast EMA 应该比 slow EMA 高(fast 跟踪快,slow 还没追上来)
|
||
if ratio <= 1.0 {
|
||
t.Errorf("RTT 突增后 RTTRatio = %.2f, 应该 > 1.0", ratio)
|
||
}
|
||
|
||
t.Logf("RTT 突增后: ratio=%.2f, fast=%v", ratio, m.RTTFast())
|
||
}
|
||
|
||
func TestScanMetrics_RTT_InsufficientSamples(t *testing.T) {
|
||
m := &ScanMetrics{}
|
||
|
||
// 少于 20 个样本
|
||
for i := 0; i < 10; i++ {
|
||
m.RecordConnect(time.Millisecond)
|
||
}
|
||
|
||
ratio := m.RTTRatio()
|
||
if ratio != 1.0 {
|
||
t.Errorf("样本不足时 RTTRatio = %.2f, 应该是 1.0", ratio)
|
||
}
|
||
}
|
||
|
||
// =============================================================================
|
||
// 并发安全测试
|
||
// =============================================================================
|
||
|
||
func TestScanMetrics_ConcurrentSafety(t *testing.T) {
|
||
m := &ScanMetrics{}
|
||
var wg sync.WaitGroup
|
||
|
||
for i := 0; i < 100; i++ {
|
||
wg.Add(4)
|
||
go func() { defer wg.Done(); m.RecordConnect(time.Millisecond) }()
|
||
go func() { defer wg.Done(); m.RecordRefused(time.Millisecond) }()
|
||
go func() { defer wg.Done(); m.RecordTimeout() }()
|
||
go func() { defer wg.Done(); m.RecordExhausted() }()
|
||
}
|
||
|
||
wg.Wait()
|
||
|
||
if m.Total() != 400 {
|
||
t.Errorf("并发后 Total() = %d, want 400", m.Total())
|
||
}
|
||
|
||
// 验证 Snapshot 不 panic
|
||
snap := m.Snapshot()
|
||
if snap.Total() != 400 {
|
||
t.Errorf("并发后 Snapshot.Total() = %d, want 400", snap.Total())
|
||
}
|
||
|
||
// 验证 RTTRatio 不 panic
|
||
_ = m.RTTRatio()
|
||
}
|