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
93 lines
3.8 KiB
Python
93 lines
3.8 KiB
Python
# ------------------------------------------------------------------------
|
|
# RF-DETR
|
|
# Copyright (c) 2025 Roboflow. All Rights Reserved.
|
|
# Licensed under the Apache License, Version 2.0 [see LICENSE for details]
|
|
# ------------------------------------------------------------------------
|
|
"""Tests for ``RFDETR.export_for_roboflow``.
|
|
|
|
``export_for_roboflow`` is the extracted, network-free core of ``deploy_to_roboflow``: it writes ``weights.pt`` (a dict
|
|
with ``"model"`` and ``"args"`` keys) and ``class_names.txt`` into a target directory. A lightweight stub stands in for
|
|
``self.model`` so the file-writing contract is exercised without building a real model or downloading weights.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import torch
|
|
|
|
from rfdetr.detr import RFDETR
|
|
|
|
|
|
def _make_stub_model(class_names: list[str]) -> RFDETR:
|
|
"""Build an RFDETR instance whose model/state are stubbed for export_for_roboflow.
|
|
|
|
``RFDETR.__init__`` is bypassed; only the attributes ``export_for_roboflow`` reads are populated.
|
|
"""
|
|
instance = RFDETR.__new__(RFDETR)
|
|
args = SimpleNamespace(resolution=560)
|
|
inner_module = SimpleNamespace(state_dict=lambda: {"weight": torch.zeros(2, 2)})
|
|
instance.model = SimpleNamespace(model=inner_module, args=args, class_names=class_names)
|
|
return instance
|
|
|
|
|
|
class TestExportForRoboflow:
|
|
"""export_for_roboflow writes a deploy-ready bundle into a directory."""
|
|
|
|
def test_writes_weights_pt_with_model_and_args(self, tmp_path: Path) -> None:
|
|
"""weights.pt is a dict with 'model' and 'args', and args carries resolution."""
|
|
model = _make_stub_model(["cat", "dog"])
|
|
|
|
model.export_for_roboflow(str(tmp_path))
|
|
|
|
bundle = torch.load(tmp_path / "weights.pt", map_location="cpu", weights_only=False)
|
|
assert set(bundle) == {"model", "args"}
|
|
assert "weight" in bundle["model"]
|
|
assert bundle["args"].resolution == 560
|
|
|
|
def test_writes_class_names_txt(self, tmp_path: Path) -> None:
|
|
"""class_names.txt lists one class name per line."""
|
|
model = _make_stub_model(["cat", "dog"])
|
|
|
|
model.export_for_roboflow(str(tmp_path))
|
|
|
|
assert (tmp_path / "class_names.txt").read_text(encoding="utf-8") == "cat\ndog"
|
|
|
|
def test_embeds_class_names_in_args(self, tmp_path: Path) -> None:
|
|
"""class_names are embedded in the saved args namespace when absent."""
|
|
model = _make_stub_model(["cat", "dog"])
|
|
|
|
model.export_for_roboflow(str(tmp_path))
|
|
|
|
bundle = torch.load(tmp_path / "weights.pt", map_location="cpu", weights_only=False)
|
|
assert bundle["args"].class_names == ["cat", "dog"]
|
|
|
|
def test_does_not_overwrite_existing_args_class_names(self, tmp_path: Path) -> None:
|
|
"""args.class_names already set on the model is preserved in the saved bundle."""
|
|
model = _make_stub_model(["cat", "dog"])
|
|
model.model.args.class_names = ["pre_existing"]
|
|
|
|
model.export_for_roboflow(str(tmp_path))
|
|
|
|
bundle = torch.load(tmp_path / "weights.pt", map_location="cpu", weights_only=False)
|
|
assert bundle["args"].class_names == ["pre_existing"]
|
|
|
|
def test_empty_class_names_writes_empty_file(self, tmp_path: Path) -> None:
|
|
"""Empty class_names list produces an empty class_names.txt (no trailing newline)."""
|
|
model = _make_stub_model([])
|
|
|
|
model.export_for_roboflow(str(tmp_path))
|
|
|
|
assert (tmp_path / "class_names.txt").read_text(encoding="utf-8") == ""
|
|
|
|
def test_creates_output_dir_when_missing(self, tmp_path: Path) -> None:
|
|
"""output_dir is created if it does not already exist."""
|
|
model = _make_stub_model(["cat", "dog"])
|
|
target = tmp_path / "nested" / "bundle"
|
|
|
|
model.export_for_roboflow(str(target))
|
|
|
|
assert (target / "weights.pt").exists()
|
|
assert (target / "class_names.txt").exists()
|