Files
fscan/core/adaptive_pool_test.go
ZacharyZcR 71b92d4408 feat: v2.1.0 核心重构与功能增强
## 架构重构
- 全局变量消除,迁移至 Config/State 对象
- SMB 插件融合(smb/smb2/smbghost/smbinfo)
- 服务探测重构,实现 Nmap 风格 fallback 机制
- 输出系统重构,TXT 实时刷盘 + 双写机制
- i18n 框架升级至 go-i18n

## 性能优化
- 正则表达式预编译
- 内存优化 map[string]struct{}
- 并发指纹匹配
- SOCKS5 连接复用
- 滑动窗口调度 + 自适应线程池

## 新功能
- Web 管理界面
- 多格式 POC 适配(xray/afrog)
- 增强指纹库(3139条)
- Favicon hash 指纹识别
- 插件选择性编译(Build Tags)
- fscan-lab 靶场环境
- 默认端口扩展(62→133)

## 构建系统
- 添加 no_local tag 支持排除本地插件
- 多版本构建:fscan/fscan-nolocal/fscan-web
- CI 添加 snapshot 模式支持仅测试构建

## Bug 修复
- 修复 120+ 个问题,包括 RDP panic、批量扫描漏报、
  JSON 输出格式、Redis 检测、Context 超时等

## 测试增强
- 单元测试覆盖率 74-100%
- 并发安全测试
- 集成测试(Web/端口/服务/SSH/ICMP)
2026-01-11 20:16:23 +08:00

238 lines
6.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package core
/*
adaptive_pool_test.go - AdaptivePool 高价值测试
测试重点:
1. 并发安全 - 多goroutine同时调整不崩溃
2. 降级逻辑 - 资源耗尽率高时正确减少线程
3. 恢复逻辑 - 资源耗尽率低时正确增加线程
4. 边界条件 - 不超过minSize/maxSize
不测试:
- 简单的getter方法太简单不值得
- ants库本身的正确性库作者负责
*/
import (
"testing"
"time"
"github.com/shadow1ng/fscan/common"
)
// =============================================================================
// 场景1降级逻辑测试高价值
// =============================================================================
// TestAdaptivePool_DowngradeOnHighExhaustion 验证资源耗尽率高时降低线程数
// 这是个核心业务逻辑:耗尽率 > 10% 时应该减少线程
func TestAdaptivePool_DowngradeOnHighExhaustion(t *testing.T) {
state := common.NewState()
pool, err := NewAdaptivePool(100, func(interface{}) {}, state)
if err != nil {
t.Fatalf("创建线程池失败: %v", err)
}
defer pool.Release()
initialCap := pool.Cap()
// 模拟高资源耗尽率20% 的包都失败了
// 需要至少100个样本才会触发调整
for i := 0; i < 200; i++ {
state.IncrementPacketCount()
if i < 40 { // 前40个失败20%
state.IncrementResourceExhaustedCount()
}
}
// 触发调整提交足够多的任务让maybeAdjust被调用
for i := 0; i < 20; i++ {
_ = pool.Invoke(nil)
time.Sleep(time.Millisecond * 10) // 等待异步调整
}
// 等待调整完成
time.Sleep(time.Millisecond * 50)
finalCap := pool.Cap()
// 验证:线程数应该减少
if finalCap >= initialCap {
t.Errorf("应该降级: 初始 %d, 最终 %d", initialCap, finalCap)
}
// 验证不应该降到minSize以下
minSize := initialCap / 4
if minSize < 10 {
minSize = 10
}
if finalCap < minSize {
t.Errorf("降到minSize以下: %d < %d", finalCap, minSize)
}
t.Logf("降级成功: %d -> %d (min=%d)", initialCap, finalCap, minSize)
}
// =============================================================================
// 场景3恢复逻辑测试高价值
// =============================================================================
// TestAdaptivePool_NoRecoveryOnLowExhaustion 验证低耗尽率时不升级
// 防止线程数盲目增长
func TestAdaptivePool_NoRecoveryOnLowExhaustion(t *testing.T) {
state := common.NewState()
pool, err := NewAdaptivePool(50, func(interface{}) {}, state)
if err != nil {
t.Fatalf("创建线程池失败: %v", err)
}
defer pool.Release()
// 先降到minSize
for i := 0; i < 500; i++ {
state.IncrementPacketCount()
state.IncrementResourceExhaustedCount() // 100% 耗尽
}
for i := 0; i < 20; i++ {
_ = pool.Invoke(nil)
}
time.Sleep(time.Millisecond * 50)
reducedCap := pool.Cap()
// 现在模拟低耗尽率只有1%失败
for i := 0; i < 500; i++ {
state.IncrementPacketCount()
if i%100 == 0 { // 只有5个失败1%
state.IncrementResourceExhaustedCount()
}
}
for i := 0; i < 20; i++ {
_ = pool.Invoke(nil)
}
time.Sleep(time.Millisecond * 50)
finalCap := pool.Cap()
// 验证:即使耗尽率低,也不应该立即恢复(保守策略)
// 或者即使恢复,也很有限
if finalCap > reducedCap+5 {
t.Logf("恢复行为: %d -> %d", reducedCap, finalCap)
}
}
// =============================================================================
// 场景4边界条件测试中价值
// =============================================================================
// TestAdaptivePool_MinSizeBoundary 验证不会降到minSize以下
func TestAdaptivePool_MinSizeBoundary(t *testing.T) {
state := common.NewState()
// 创建小线程池minSize会是10
pool, err := NewAdaptivePool(40, func(interface{}) {}, state)
if err != nil {
t.Fatalf("创建线程池失败: %v", err)
}
defer pool.Release()
// 模拟极端的资源耗尽100%失败
for i := 0; i < 1000; i++ {
state.IncrementPacketCount()
state.IncrementResourceExhaustedCount()
}
// 触发多次调整
for i := 0; i < 50; i++ {
_ = pool.Invoke(nil)
time.Sleep(time.Millisecond)
}
finalCap := pool.Cap()
// 验证不应该低于10
if finalCap < 10 {
t.Errorf("线程数 < 10: %d", finalCap)
}
t.Logf("最小边界测试通过: cap=%d", finalCap)
}
// =============================================================================
// 场景5样本不足测试低价值但重要
// =============================================================================
// TestAdaptivePool_NotEnoughSamples 验证样本不足时不调整
// 防止基于小样本做错误决策
func TestAdaptivePool_NotEnoughSamples(t *testing.T) {
state := common.NewState()
pool, err := NewAdaptivePool(100, func(interface{}) {}, state)
if err != nil {
t.Fatalf("创建线程池失败: %v", err)
}
defer pool.Release()
initialCap := pool.Cap()
// 只增加少量样本(<100不足以触发调整
for i := 0; i < 50; i++ {
state.IncrementPacketCount()
state.IncrementResourceExhaustedCount() // 即使100%失败也不调整
}
// 提交任务
for i := 0; i < 10; i++ {
_ = pool.Invoke(nil)
}
time.Sleep(time.Millisecond * 50)
finalCap := pool.Cap()
// 验证:样本不足时不应该调整
if finalCap != initialCap {
t.Errorf("样本不足时不应该调整: %d -> %d", initialCap, finalCap)
}
}
// =============================================================================
// 辅助函数
// =============================================================================
// TestAdaptivePool_Wait 验证Wait方法正确等待所有任务完成
func TestAdaptivePool_Wait(t *testing.T) {
state := common.NewState()
pool, err := NewAdaptivePool(10, func(interface{}) {
time.Sleep(time.Millisecond * 50)
}, state)
if err != nil {
t.Fatalf("创建线程池失败: %v", err)
}
defer pool.Release()
// 提交任务
for i := 0; i < 20; i++ {
_ = pool.Invoke(nil)
}
// Wait应该在所有任务完成后返回
start := time.Now()
pool.Wait()
duration := time.Since(start)
// 20个任务每个50ms10个线程应该约100ms完成
if duration < 80*time.Millisecond {
t.Logf("Wait提前返回可能测试有问题: %v", duration)
}
if duration > 200*time.Millisecond {
t.Errorf("Wait耗时过长: %v", duration)
}
t.Logf("Wait测试通过: %v", duration)
}