mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-15 12:15:16 +00:00
test(agent/core): 消除测试对 scheduler.queue 的无锁直读,并加判据防回归
## 起因
offload_test.go 等 4 个文件里有 18 处 `len(s.queue)` / `range s.queue`,
全在锁外。当时**安全**——那些测试用 newRootWithoutSchedulerLoop,
刻意不启 schedulerLoop,所以没有并发写者。
但这个安全性 ① 不由测试自身保证,只由「别改构造函数」这个口头约定保证;
② 一旦有人换成 newRootWith 就变成真竞争,症状是「今天全绿、改一次就炸」。
先确认生产代码没问题:逐处核对 scheduler.go 里 6 个读 s.queue 的函数,
全部在 mu.Lock/RLock 内(takeQueuedInputs/hasRoom/nextRef/armStop/
pendingEvents/takeAllPendingEvents)。所以这是**测试的隐患**,不是核心缺陷。
## 改动
- scheduler.go 新增两个带锁只读访问器:
· queueLen() —— 替代 len(s.queue)
· queueSnapshot() —— 返回浅拷贝,替代 range/索引(拷贝保证 len 与
range 不会在遍历中途变化)
- 4 个测试文件 18 处直读全部改走访问器
- 新增 queue_access_test.go:AST 静态判据,扫所有 *_test.go,
同一函数内出现过 Lock/RLock 即视为持锁(Go 的 defer Unlock 静态难还原,
按函数近似足够保守);命中无锁直读即红
## 判据自检
静态判据最大的失败模式是「根本没抓到东西」,所以带双向自检:
- 正向:注入一处无锁直读 → 必须被抓到
- 反向:注入一处持锁直读 → 必须**不**被报(否则噪声大没人看)
- 真变异:把一处访问器改回 `len(s.queue)` → 红
## 验证
- go build ./... / go vet / go test ./internal/... 全绿
- go test -race ./internal/agent/core/ 无数据竞争
- 部署后:39 插件加载、3 个 multimodal 工具注册、0 panic/fatal
- graph.db integrity ok,blocks 2787 未动
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -155,11 +155,11 @@ func TestScheduler_Lifecycle(t *testing.T) {
|
||||
if t1 == nil || s.running != t1 {
|
||||
t.Fatal("next 应取出任务并置为 running")
|
||||
}
|
||||
if len(s.queue) != 1 {
|
||||
t.Fatalf("取出后队列长度=%d,期望 1", len(s.queue))
|
||||
if n := s.queueLen(); n != 1 {
|
||||
t.Fatalf("取出后队列长度=%d,期望 1", n)
|
||||
}
|
||||
// 队列内不得同时出现 running(O1:三集合互不重叠)。
|
||||
for _, q := range s.queue {
|
||||
for _, q := range s.queueSnapshot() {
|
||||
if q == t1 {
|
||||
t.Fatal("running 任务不得同时留在就绪队列")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user