Files
ModelTrainEval/tests/test_config.py
T
Shaun ZhouandClaude Opus 5 d6adea5b05 test: 单元测试与端到端冒烟
88 个测试覆盖配置继承、注册表、GPU 守卫、YOLO 格式解析、数据集、模型形状、
标签模式、聚类原语与 SimCLR 损失,以及 train.py / eval.py / 聚类脚本的入口接线。

含两条回归测试:
- PCA 白化放大噪声方向导致簇塌陷(默认 whiten=False 的依据)
- classes 写成 data.yaml 路径时 num_classes 必须是类别数而非路径字符串长度

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:55:47 +08:00

49 lines
1.7 KiB
Python

import pytest
pytest.importorskip("yaml")
from utils.config import Config, apply_overrides, deep_merge, load_config, parse_override # noqa: E402
def test_deep_merge_is_recursive():
merged = deep_merge({"a": {"b": 1, "c": 2}}, {"a": {"c": 3}, "d": 4})
assert merged == {"a": {"b": 1, "c": 3}, "d": 4}
def test_parse_override_literals():
assert parse_override("train.lr=1e-3") == ("train.lr", 1e-3)
assert parse_override("train.amp=False") == ("train.amp", False)
assert parse_override("model.backbone.name=tiny_conv") == ("model.backbone.name", "tiny_conv")
def test_apply_overrides_creates_missing_nodes():
cfg = apply_overrides({}, ["a.b.c=1"])
assert cfg == {"a": {"b": {"c": 1}}}
def test_load_config_with_base(tmp_path):
(tmp_path / "base.yaml").write_text("train:\n lr: 0.1\n epochs: 10\n", encoding="utf-8")
(tmp_path / "child.yaml").write_text(
"_base_: ./base.yaml\ntrain:\n lr: 0.01\nwork_dir: runs/x\n", encoding="utf-8"
)
cfg = load_config(tmp_path / "child.yaml", ["train.epochs=2"])
assert isinstance(cfg, Config)
assert cfg.train.lr == 0.01
assert cfg.train.epochs == 2
assert cfg.get_path("work_dir") == "runs/x"
def test_load_config_detects_cycle(tmp_path):
(tmp_path / "a.yaml").write_text("_base_: ./b.yaml\n", encoding="utf-8")
(tmp_path / "b.yaml").write_text("_base_: ./a.yaml\n", encoding="utf-8")
with pytest.raises(ValueError, match="循环引用"):
load_config(tmp_path / "a.yaml")
def test_project_configs_are_loadable():
cfg = load_config("configs/train/baseline.yaml")
assert cfg["model"]["name"] == "defect_net"
assert cfg["data"]["train"]["dataset"]["name"] == "film_defect"
assert len(cfg["classes"]) > 0