执行摘要
- 一句话:重构 diffusion CI,抽取公共 runner 并简化测试组织
- 推荐动作:值得精读,尤其关注如何通过抽取公共模块消除重复代码。测试基础设施重构的套路可以复用。建议团队在合并后密切监控 NPU/AMD CI,并确保文档更新反映新的文件组织结构。
功能与动机
原始 diffusion 测试代码中存在大量重复的 pytest 运行逻辑(如 collect_test_items、parse_junit_xml 等),分布于 run_suite.py、run_suite_musa.py 等多个文件中,不利于维护。同时,部分测试文件组织混乱,测试用例设计冗余。本次重构旨在统一测试基础设施,提高可维护性和可扩展性,并为后续平台(NPU/AMD)的 CI 集成打下基础。
实现拆解
-
创建公共 runner:在 python/sglang/multimodal_gen/test/runner/pytest_runner.py 中汇集了所有与 pytest 运行相关的工具函数,包括 collect_test_items、parse_junit_xml_for_executed_cases、parse_junit_xml_for_case_results、以及内部重试辅助函数 _run_pytest_attempt、_extract_collection_line、_extract_short_test_summary、_extract_failure_tail、_summary_has_retryable_failure。
-
删除 musa 专用 runner:删除了独立的 run_suite_musa.py(270 行),新增 server/musa/run_suite.py(172 行),该文件复用公共 runner,实现了相同的套件分区执行逻辑,但代码量大幅减少。
-
简化主 runner:修改 run_suite.py,将其内联的 collect_test_items、parse_junit_xml_for_executed_cases、parse_junit_xml_for_case_results 等函数定义删除(约 357 行),改为从 runner.pytest_runner 导入 partition_items_by_index 和 run_pytest。此外,将 write_execution_report 和 run_component_accuracy_files 等逻辑保留在原地但进行了精简。
-
测试文件重组织:将 test_update_weights_from_disk.py 从 server/ 移动到 single_test_file/,并将原来的三个单独测试(nonexistent_model、missing_model_path、nonexistent_module)合并为一个参数化的 test_update_weights_rejects_invalid_requests,同时更新了文档注释。类似地,cli_generate_common.py 从 cli/ 移动到 single_test_file/,test_consistency_metrics.py 从根测试目录移动到 unit/。
-
清理旧文件:删除了不再使用的 cli/test_generate_t2i_perf.py(23 行),并更新了 .github/workflows 中的 CI 配置以反映新的运行器路径。
-
基准数据更新:由于移除了 LTX2 的 snapshot 模式(改为弃用别名),重新生成了 ltx_2_3_hq 的一致性测试基准和性能基准,将 CI 的 GT 快照指向新生成的原始模式结果。
关键文件:
python/sglang/multimodal_gen/test/runner/pytest_runner.py(模块 测试运行器;类别 test;类型 test-coverage;符号 collect_test_items, parse_junit_xml_for_executed_cases, parse_junit_xml_for_case_results, _run_pytest_attempt): 核心新增文件,抽取了所有公共 pytest 运行逻辑,是整个重构的基石。
python/sglang/multimodal_gen/test/run_suite_musa.py(模块 MUSA 运行器;类别 test;类型 deletion;符号 parse_args, collect_test_items, run_pytest, main): 被删除的独立 musa runner,体现了代码冗余的消除。
python/sglang/multimodal_gen/test/run_suite.py(模块 主运行器;类别 test;类型 test-coverage;符号 collect_test_items, parse_junit_xml_for_executed_cases, parse_junit_xml_for_case_results, _run_pytest_attempt): 主运行器,删除了 357 行重复定义,改为导入公共模块,是重构的核心体现。
python/sglang/multimodal_gen/test/server/musa/run_suite.py(模块 MUSA 运行器;类别 test;类型 test-coverage;符号 parse_args, _resolve_suite_files, main): 新增的 musa runner,替代删除的 run_suite_musa.py,代码量精简且复用公共 runner。
python/sglang/multimodal_gen/test/single_test_file/test_update_weights_from_disk.py(模块 权重更新测试;类别 test;类型 rename-or-move;符号 test_update_weights_nonexistent_model, test_update_weights_rejects_invalid_requests, test_update_weights_missing_model_path, test_update_weights_nonexistent_module): 移动并简化了测试用例,体现了测试组织优化。
关键符号:collect_test_items, parse_junit_xml_for_executed_cases, parse_junit_xml_for_case_results, _run_pytest_attempt, _extract_collection_line, _extract_short_test_summary, _extract_failure_tail, _summary_has_retryable_failure, partition_items_by_index, run_pytest
关键源码片段
python/sglang/multimodal_gen/test/runner/pytest_runner.py
核心新增文件,抽取了所有公共 pytest 运行逻辑,是整个重构的基石。
def collect_test_items(
files: Sequence[str], filter_expr: str | None = None
) -> list[str]:
"""Collect pytest node IDs from the given files or node selectors."""
# 构造 pytest --collect-only 命令,-q 减少输出
cmd = [sys.executable, "-m", "pytest", "--collect-only", "-q"]
if filter_expr:
cmd.extend(["-k", filter_expr])
cmd.extend(files)
filter_note = f" with filter: {filter_expr}" if filter_expr else ""
print(f"Collecting tests from {len(files)} item(s){filter_note}")
result = subprocess.run(cmd, capture_output=True, text=True)
# pytest 返回码 5 表示未收集到测试,是预期行为
if result.returncode not in (0, 5):
error_msg = (
f"pytest --collect-only failed with exit code {result.returncode}\n"
f"Command: {' '.join(cmd)}\n"
)
if result.stderr:
error_msg += f"stderr:\n{result.stderr}\n"
if result.stdout:
error_msg += f"stdout:\n{result.stdout}\n"
raise RuntimeError(error_msg)
if result.returncode == 5:
print(
"No tests were collected (exit code 5). This may be expected with filters."
)
# 解析 stdout 提取测试 ID(形如 file::class::method 的行)
test_items = []
for line in result.stdout.strip().split("\n"):
line = line.strip()
if line and "::" in line and not line.startswith(("=", "-", " ")):
test_id = line.split()[0] if " " in line else line
if "::" in test_id:
test_items.append(test_id)
print(f"Collected {len(test_items)} test items")
return test_items
评论区精华
PR 作者 mickqian 在评论中 @ping1jing2 @bingxche 询问重构是否会影响 NPU/AMD CI(见 PR 评论)。目前没有进一步的讨论或 review 反馈,表明可能已通过内部沟通确认安全。
- 重构是否影响 NPU/AMD CI (question): 暂未看到回复,但 PR 已合并,推测已通过其他渠道确认无影响。
风险与影响
- 风险:
- 测试 runner 回归:重构后的
pytest_runner.py 可能引入新的 bug,导致测试收集、执行或结果解析出现错误,尤其是 _run_pytest_attempt 的流式读取和重试逻辑需要仔细验证。
- 文件移动导致路径依赖失效:多个测试文件被移动(如
test_update_weights_from_disk.py 从 server/ 移到 single_test_file/),若其他脚本或 CI 配置硬编码了旧路径,可能导致测试无法找到。
- LTX2 snapshot 弃用影响:尽管保留了别名,但文档指出弃用窗口,现有使用 snapshot 模式的用户可能在未来版本中遇到兼容性问题。
- NPU/AMD 平台风险:musa runner 被重写且公共 runner 未在 NPU/AMD 上充分测试,可能导致这些平台 CI 失败。
- 影响:影响范围:主要影响 diffusion 模块的 CI 流程和测试开发者。影响程度:中等。功能上保持了等价性,但代码组织大幅改善。NPU/AMD 平台可能需要验证新的 runner 是否正常工作。LTX2 用户需要注意 snapshot 模式的弃用通知。
- 风险标记:测试核心路径变更, 文件移动影响路径解析, 平台兼容风险(NPU/AMD), LTX2 snapshot 弃用
关联脉络
参与讨论