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
111 lines
3.9 KiB
Python
111 lines
3.9 KiB
Python
# ------------------------------------------------------------------------
|
|
# RF-DETR
|
|
# Copyright (c) 2025 Roboflow. All Rights Reserved.
|
|
# Licensed under the Apache License, Version 2.0 [see LICENSE for details]
|
|
# ------------------------------------------------------------------------
|
|
|
|
from unittest.mock import Mock
|
|
|
|
import pytest
|
|
import torch
|
|
from PIL import Image
|
|
|
|
from rfdetr.export.benchmark import TRTInference, infer_transforms
|
|
|
|
|
|
class TestTRTInference:
|
|
def test_synchronize_sync_mode_does_not_require_stream(self, monkeypatch) -> None:
|
|
"""`synchronize()` should not access stream in sync mode."""
|
|
inference = TRTInference.__new__(TRTInference)
|
|
inference.sync_mode = True
|
|
|
|
mock_is_available = Mock(return_value=True)
|
|
mock_cuda_sync = Mock()
|
|
monkeypatch.setattr("torch.cuda.is_available", mock_is_available)
|
|
monkeypatch.setattr("torch.cuda.synchronize", mock_cuda_sync)
|
|
|
|
inference.synchronize()
|
|
|
|
mock_is_available.assert_called_once()
|
|
mock_cuda_sync.assert_called_once()
|
|
|
|
def test_synchronize_async_mode_uses_stream_sync(self, monkeypatch) -> None:
|
|
"""`synchronize()` should use stream synchronization in async mode."""
|
|
inference = TRTInference.__new__(TRTInference)
|
|
inference.sync_mode = False
|
|
inference.stream = Mock()
|
|
|
|
mock_cuda_sync = Mock()
|
|
monkeypatch.setattr("torch.cuda.synchronize", mock_cuda_sync)
|
|
|
|
inference.synchronize()
|
|
|
|
inference.stream.synchronize.assert_called_once()
|
|
mock_cuda_sync.assert_not_called()
|
|
|
|
def test_infer_transforms_accepts_none_target(self) -> None:
|
|
"""Benchmark inference preprocessing should support image-only input."""
|
|
image = Image.new("RGB", (320, 240))
|
|
|
|
image_tensor, target = infer_transforms()(image, None)
|
|
|
|
assert isinstance(image_tensor, torch.Tensor)
|
|
assert image_tensor.shape == (3, 640, 640)
|
|
assert image_tensor.dtype == torch.float32
|
|
assert target is None
|
|
|
|
|
|
class TestBenchmarkShapeParameterization:
|
|
"""Benchmark preprocessing/postprocessing read input size and query count instead of hardcoding 640/300."""
|
|
|
|
def test_infer_transforms_uses_requested_size(self) -> None:
|
|
"""infer_transforms resizes to the caller-supplied (height, width)."""
|
|
image = Image.new("RGB", (320, 240))
|
|
|
|
image_tensor, _ = infer_transforms((512, 384))(image, None)
|
|
|
|
assert image_tensor.shape == (3, 512, 384)
|
|
|
|
def test_infer_transforms_defaults_to_640(self) -> None:
|
|
"""The default input size stays 640x640 for callers that do not pass a size."""
|
|
image = Image.new("RGB", (320, 240))
|
|
|
|
image_tensor, _ = infer_transforms()(image, None)
|
|
|
|
assert image_tensor.shape == (3, 640, 640)
|
|
|
|
def test_static_dim_returns_concrete_int(self) -> None:
|
|
"""A concrete positive dimension is returned unchanged."""
|
|
from rfdetr.export.benchmark import _static_dim
|
|
|
|
assert _static_dim(384, 640) == 384
|
|
|
|
@pytest.mark.parametrize(
|
|
"value",
|
|
[
|
|
pytest.param("height", id="dynamic-string"),
|
|
pytest.param(None, id="none"),
|
|
pytest.param(-1, id="negative"),
|
|
],
|
|
)
|
|
def test_static_dim_falls_back_for_dynamic_axis(self, value) -> None:
|
|
"""Dynamic/unknown axes fall back to the provided default."""
|
|
from rfdetr.export.benchmark import _static_dim
|
|
|
|
assert _static_dim(value, 640) == 640
|
|
|
|
def test_post_process_respects_num_queries(self) -> None:
|
|
"""post_process selects exactly num_queries detections per image."""
|
|
from rfdetr.export.benchmark import post_process
|
|
|
|
num_queries = 5
|
|
outputs = {
|
|
"labels": torch.rand(1, 20, 3),
|
|
"dets": torch.rand(1, 20, 4),
|
|
}
|
|
target_sizes = torch.tensor([[480, 640]])
|
|
|
|
results = post_process(outputs, target_sizes, num_queries=num_queries)
|
|
|
|
assert results[0]["scores"].shape == (num_queries,)
|