fix(tokenless): fix rtk pytest 'No tests collected' regression#881
fix(tokenless): fix rtk pytest 'No tests collected' regression#881shiloong wants to merge 1 commit into
Conversation
Root cause: pytest in CI/Docker outputs ANSI escape codes
(\x1b[1m...\x1b[0m) wrapping === headers. strip_ansi was not called
before starts_with("===") checks, causing ALL state transitions to fail.
Changes:
- pytest_cmd.rs: call strip_ansi() before trim() in the filter loop
- pytest_cmd.rs: add errors field + ERRORS section + error keyword parsing
- runner.rs: stderr fallback replaces 'No tests collected' with stderr content
- runner.rs: separate display/tracking to preserve stats accuracy
Signed-off-by: Shile Zhang <shile.zhang@linux.alibaba.com>
Forrest-ly
left a comment
There was a problem hiding this comment.
Code Review — fix(tokenless): fix rtk pytest 'No tests collected' regression
Summary
此 PR 修复了 rtk pytest 输出解析器在 CI/Docker 环境中的回归问题。核心根因是 pytest 在带色彩的终端中输出 ANSI 转义码包裹 === 头部,导致状态机所有转换失败,最终误报 "No tests collected"。
变更涉及两部分:
- pytest_cmd.rs — 在匹配前调用
strip_ansi();新增errors字段和 ERRORS section 解析 - runner.rs — stderr 回退逻辑重构,分离 display/tracking 以保持统计准确性
正面评价
- 根因定位准确:ANSI 转义码问题是典型的 CI 环境陷阱,
strip_ansi在trim()前调用是正确且最小化的修复 - 测试覆盖充分:8 个新增测试用例覆盖了 errors section header、mixed errors/failures、empty stdout、raw traceback 等边界场景
- display/tracking 分离设计合理:避免 stderr 内容污染 token savings 统计
问题与建议
1. [Medium] filtered.clone() 无条件执行(runner.rs)
let display = if opts.filter_stdout_only && ... {
// stderr fallback
preview
} else {
filtered.clone() // ← 所有命令都付出 clone 代价
};filtered 后续仅在 timer.track(filtered, ...) 中被消费(moved)。对于非 pytest 命令(filter_stdout_only=false),这里产生了一次不必要的 String 克隆。建议改为:
let (display, tracked) = if opts.filter_stdout_only && ... {
(preview, filtered.clone())
} else {
(filtered.clone(), filtered)
};或者使用 Cow<str> 避免 else 分支的分配。考虑到实际命令输出可能较大(几十 KB),值得优化。
2. [Low] pytest_cmd.rs 与 runner.rs 之间的隐式耦合
pytest_cmd.rs 在 stdout 为空时返回:
"Pytest: No tests collected (empty stdout, see stderr below)"
而 runner.rs 通过 filtered.contains("No tests collected") 来触发 stderr 回退。这个 contains 检查恰好能匹配带后缀的版本,但两个文件之间靠子串匹配隐式耦合。如果未来 pytest_cmd.rs 修改措辞(比如改为 "No tests found"),runner.rs 会静默失效。
建议:在 filter_pytest_output 返回值中增加一个结构化标记(如 enum 或 bool 字段),而非依赖字符串子串判断。
3. [Low] parse_summary_line 中 error 关键词匹配
} else if *word == "error" || *word == "errors" {
counts.errors = n;
}这里用精确匹配 == 是正确且安全的(与其他字段用 .contains() 不同),因为 pytest summary 中 error 可能与 "collection error" 等组合出现。当前实现能正确处理 "1 error" 和 "2 errors",但无法处理 "1 collection error" 这种 pytest 偶尔输出的格式。如果有此类 case,建议回退到 .contains("error")。
4. [Nit] patch 文件的 index hash
旧 patch:index 2febb2e..2816158
新 patch:index 2febb2e..773cb55
目标 hash 变了说明 patch 应用后的结果不同,这是预期的。但建议在 PR 描述中说明这个 patch 是基于 rtk 的哪个 commit 生成的,以便后续维护者验证 patch 是否仍然适用。
总结
这是一个针对真实 CI 环境问题的有效修复,根因分析清晰,测试覆盖到位。主要关注点是 runner.rs 中的无条件 clone 和跨文件隐式耦合。建议处理 #1(性能)和 #2(可维护性)后合入。
Verdict: Approve with minor suggestions — 不阻塞合入,但建议 follow-up 优化。
Code Review — PR #881结论:✅ Approve 优点
建议(非阻塞)
|
Description
Root cause: pytest in CI/Docker outputs ANSI escape codes (\x1b[1m...\x1b[0m) wrapping === headers. strip_ansi was not called before starts_with("===") checks, causing ALL state transitions to fail.
Changes:
Related Issue
fixes # bugs found in benchmarking
Type of Change
Scope
cosh(copilot-shell)sec-core(agent-sec-core)skill(os-skills)sight(agentsight)tokenless(tokenless)ckpt(ws-ckpt)memory(agent-memory)anolisa(anolisa-cli)Checklist
cosh: Lint passes, type check passes, and tests passsec-core(Rust):cargo clippy -- -D warningsandcargo fmt --checkpasssec-core(Python): Ruff format and pytest passskill: Skill directory structure is valid and shell scripts pass syntax checksight:cargo clippy -- -D warningsandcargo fmt --checkpasstokenless:cargo clippy -- -D warningsandcargo fmt --checkpassmemory(Linux only):cargo clippy --all-targets -- -D warnings,cargo fmt --check, andcargo testpassanolisa:cargo clippy --all-targets --locked -- -D warnings,cargo fmt --all --check, andcargo test --lockedpasspackage-lock.json/Cargo.lock)Testing
Additional Notes