Files
wehub-resource-sync 16031aae96
CPU tests Workflow / Testing (ubuntu-latest, 3.12) (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.13) (push) Failing after 0s
Mypy Type Check / Type Check (push) Failing after 0s
Docs/Test WorkFlow / Test docs build (push) Failing after 1s
PR Conflict Labeler / labeling (push) Failing after 1s
Dependency resolution / Resolve [tflite] extra — Python 3.12 (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.10) (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.13) (push) Failing after 1s
CPU tests Workflow / build-pkg (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.10) (push) Failing after 0s
CPU tests Workflow / Testing (ubuntu-latest, 3.11) (push) Failing after 0s
Smoke Tests / try-all-models (macos-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (macos-latest, 3.13) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / testing-guardian (push) Has been cancelled
GPU tests Workflow / Testing (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:26:24 +08:00

196 lines
6.8 KiB
Python

# ------------------------------------------------------------------------
# RF-DETR
# Copyright (c) 2025 Roboflow. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 [see LICENSE for details]
# ------------------------------------------------------------------------
"""CLI smoke tests and YAML roundtrip tests — PTL Ch4/T7.
Smoke tests run RFDETRCli in-process with args=['--help'] / ['fit', '--help'] / ['validate', '--help'] and assert
SystemExit(0) — no subprocess needed.
YAML roundtrip tests load each example config with yaml.safe_load, import the class_path, construct the config object
with the YAML init_args, and verify every specified field survived the round-trip.
"""
import importlib
import pathlib
import pytest
import yaml
CONFIGS_DIR = pathlib.Path(__file__).parent.parent.parent / "configs"
ALL_CONFIGS = [
"rfdetr_nano",
"rfdetr_small",
"rfdetr_medium",
"rfdetr_base",
"rfdetr_large",
"rfdetr_seg_nano",
"rfdetr_seg_small",
"rfdetr_seg_medium",
"rfdetr_seg_large",
"rfdetr_seg_xlarge",
"rfdetr_seg_2xlarge",
]
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _run_cli(*args: str) -> int:
"""Run RFDETRCli in-process with the given args; return the SystemExit code."""
from rfdetr.training.cli import RFDETRCli
from rfdetr.training.module_data import RFDETRDataModule
from rfdetr.training.module_model import RFDETRModelModule
with pytest.raises(SystemExit) as exc_info:
RFDETRCli(RFDETRModelModule, RFDETRDataModule, args=list(args))
return exc_info.value.code
def _load(name: str) -> dict:
return yaml.safe_load((CONFIGS_DIR / f"{name}.yaml").read_text())
def _instantiate(class_path: str, init_args: dict) -> object:
"""Import class_path and construct an instance with init_args."""
module_path, class_name = class_path.rsplit(".", 1)
cls = getattr(importlib.import_module(module_path), class_name)
return cls(**init_args)
# ---------------------------------------------------------------------------
# Smoke tests
# ---------------------------------------------------------------------------
class TestCLIEntrypoint:
"""Module entrypoint and CLI import tests."""
def test_python_module_entrypoint_runs(self) -> None:
"""Python -m rfdetr --help exits 0 and mentions rfdetr."""
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-m", "rfdetr", "--help"],
capture_output=True,
text=True,
timeout=30,
)
assert result.returncode == 0
assert "rfdetr" in result.stdout.lower() or "rfdetr" in result.stderr.lower()
def test_cli_main_is_importable(self) -> None:
"""rfdetr.cli module is importable and exposes a callable main."""
import importlib
mod = importlib.import_module("rfdetr.cli")
assert callable(getattr(mod, "main", None))
class TestCLIHelp:
"""Rfdetr --help and subcommand --help must exit 0."""
def test_top_level_help(self):
"""Rfdetr --help exits with code 0."""
assert _run_cli("--help") == 0
def test_fit_help(self):
"""Rfdetr fit --help exits with code 0."""
assert _run_cli("fit", "--help") == 0
def test_validate_help(self):
"""Rfdetr validate --help exits with code 0."""
assert _run_cli("validate", "--help") == 0
def test_fit_help_exposes_model_config(self):
"""Rfdetr fit --help output lists model.model_config arguments."""
import io
import sys
buf = io.StringIO()
old_stdout = sys.stdout
sys.stdout = buf
try:
_run_cli("fit", "--help")
finally:
sys.stdout = old_stdout
assert "model_config" in buf.getvalue()
def test_fit_help_exposes_train_config(self):
"""Rfdetr fit --help output lists model.train_config arguments."""
import io
import sys
buf = io.StringIO()
old_stdout = sys.stdout
sys.stdout = buf
try:
_run_cli("fit", "--help")
finally:
sys.stdout = old_stdout
assert "train_config" in buf.getvalue()
# ---------------------------------------------------------------------------
# YAML roundtrip — model_config
# ---------------------------------------------------------------------------
class TestModelConfigRoundtrip:
"""model_config init_args from YAML construct a valid config with matching values."""
@pytest.mark.parametrize("name", ALL_CONFIGS)
def test_model_config_fields_survive_roundtrip(self, name):
"""Every field in model_config.init_args is preserved after instantiation."""
data = _load(name)
mc_section = data["model"]["model_config"]
class_path = mc_section["class_path"]
init_args = mc_section.get("init_args", {})
mc = _instantiate(class_path, init_args)
for field, value in init_args.items():
assert getattr(mc, field) == value, (
f"{name}.yaml: model_config.{field} expected {value!r}, got {getattr(mc, field)!r}"
)
# ---------------------------------------------------------------------------
# YAML roundtrip — train_config
# ---------------------------------------------------------------------------
class TestTrainConfigRoundtrip:
"""train_config init_args from YAML construct a valid config with matching values."""
@pytest.mark.parametrize("name", ALL_CONFIGS)
def test_train_config_fields_survive_roundtrip(self, name, tmp_path):
"""Every field in train_config.init_args is preserved after instantiation.
dataset_dir is rewritten to tmp_path so path expansion doesn't fail on the placeholder /data/coco value.
TrainConfig.expand_paths() converts relative paths containing separators to absolute, so both sides are
normalised with os.path.abspath before comparison.
"""
import os
data = _load(name)
tc_section = data["model"]["train_config"]
class_path = tc_section["class_path"]
init_args = dict(tc_section.get("init_args", {}))
# Substitute placeholder dataset_dir with a real tmp_path.
init_args["dataset_dir"] = str(tmp_path)
tc = _instantiate(class_path, init_args)
for field, value in init_args.items():
actual = getattr(tc, field)
# TrainConfig.expand_paths() resolves relative path strings to
# absolute, so normalise both sides for string path fields.
if isinstance(value, str) and (os.sep in value or "/" in value):
value = os.path.abspath(value)
assert actual == value, f"{name}.yaml: train_config.{field} expected {value!r}, got {actual!r}"