Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,23 @@ venv.bak/

# mypy
.mypy_cache/

# Claude Code settings
.claude/

# Additional testing artifacts
.pytest_cache/
htmlcov/
coverage.xml
.coverage

# IDE files
.vscode/
.idea/
*.swp
*.swo
*~

# OS files
.DS_Store
Thumbs.db
1,945 changes: 1,945 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
[tool.poetry]
name = "rts3d"
version = "0.1.0"
description = "RTS3D: Real-time 3D object detection from monocular images"
authors = ["Your Name <your.email@example.com>"]
readme = "README.md"
packages = [{include = "src"}]

[tool.poetry.dependencies]
python = "^3.8"
opencv-python = "*"
Cython = "*"
numba = "*"
progress = "*"
matplotlib = "*"
easydict = "*"
scipy = "*"
pycocotools = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
pytest-cov = "^4.1.0"
pytest-mock = "^3.11.1"

# Custom commands can be run with 'poetry run <command>'

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.pytest.ini_options]
minversion = "6.0"
addopts = "-ra -q --strict-markers --strict-config"
testpaths = ["tests"]
markers = [
"unit: marks tests as unit tests (fast, isolated)",
"integration: marks tests as integration tests (slower, require setup)",
"slow: marks tests as slow running tests"
]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]

[tool.coverage.run]
source = ["src"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/setup.py",
"*/conftest.py",
"src/lib/external/*",
"src/lib/utils/iou3d/src/*"
]
branch = true

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod"
]
ignore_errors = true
show_missing = true
precision = 2
fail_under = 80

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"
Empty file added tests/__init__.py
Empty file.
125 changes: 125 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"""
Shared pytest fixtures for the RTS3D project test suite.
"""
import os
import tempfile
import pytest
from pathlib import Path
from unittest.mock import Mock
import numpy as np


@pytest.fixture
def temp_dir():
"""Create a temporary directory for test files."""
with tempfile.TemporaryDirectory() as tmp_dir:
yield Path(tmp_dir)


@pytest.fixture
def sample_image_path(temp_dir):
"""Create a sample test image file."""
image_path = temp_dir / "test_image.jpg"
# Create a dummy image file (just touching for testing infrastructure)
image_path.touch()
return image_path


@pytest.fixture
def sample_kitti_data():
"""Provide sample KITTI format data for testing."""
return {
'bbox': [100, 200, 300, 400], # [x1, y1, x2, y2]
'location': [1.0, 2.0, 3.0], # [x, y, z]
'dimensions': [1.5, 1.8, 4.0], # [height, width, length]
'rotation_y': 0.1,
'score': 0.95
}


@pytest.fixture
def mock_model():
"""Mock model for testing without loading actual weights."""
mock = Mock()
mock.eval.return_value = mock
mock.cuda.return_value = mock
return mock


@pytest.fixture
def mock_opts():
"""Mock options/configuration for testing."""
opts = Mock()
opts.device = 'cpu'
opts.load_model = ''
opts.arch = 'rts3d_34'
opts.heads = {'hm': 3, 'wh': 2, 'reg': 2, 'dep': 1, 'rot': 8, 'dim': 3, 'amodel_offset': 2}
opts.down_ratio = 4
opts.input_res = 512
opts.output_res = 128
opts.K = 100
opts.test_focal_length = 721.5377
opts.pad = 31
opts.num_stacks = 1
opts.dense_wh = False
opts.cat_spec_wh = False
opts.not_reg_offset = False
return opts


@pytest.fixture
def sample_tensor():
"""Provide a sample tensor for testing."""
return np.random.rand(3, 512, 512).astype(np.float32)


@pytest.fixture
def kitti_annotation_line():
"""Sample KITTI annotation line."""
return "Car 0.00 0 -1.57 599.41 156.40 629.75 189.25 2.85 1.63 8.06 2.85 1.47 69.44 -1.56"


@pytest.fixture
def test_data_dir(temp_dir):
"""Create a test data directory structure."""
data_dir = temp_dir / "data"
data_dir.mkdir()

# Create subdirectories
(data_dir / "images").mkdir()
(data_dir / "labels").mkdir()
(data_dir / "calib").mkdir()

return data_dir


@pytest.fixture(scope="session")
def project_root():
"""Get the project root directory."""
return Path(__file__).parent.parent


@pytest.fixture
def mock_logger():
"""Mock logger for testing."""
mock = Mock()
mock.info = Mock()
mock.debug = Mock()
mock.warning = Mock()
mock.error = Mock()
return mock


@pytest.fixture
def disable_cuda(monkeypatch):
"""Disable CUDA for CPU-only testing."""
monkeypatch.setattr('torch.cuda.is_available', lambda: False)


@pytest.fixture(autouse=True)
def clean_environment():
"""Clean environment variables before each test."""
original_env = os.environ.copy()
yield
os.environ.clear()
os.environ.update(original_env)
Empty file added tests/integration/__init__.py
Empty file.
100 changes: 100 additions & 0 deletions tests/test_infrastructure_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"""
Validation tests to verify the testing infrastructure is working correctly.
These tests validate that pytest, coverage, and fixtures are set up properly.
"""
import pytest
import sys
from pathlib import Path


class TestInfrastructureValidation:
"""Test suite to validate the testing infrastructure setup."""

def test_pytest_working(self):
"""Verify pytest is working correctly."""
assert True

def test_python_version(self):
"""Verify Python version is appropriate."""
assert sys.version_info >= (3, 8), "Python 3.8+ is required"

def test_project_structure(self, project_root):
"""Verify project structure is accessible."""
assert project_root.exists()
assert (project_root / "src").exists()
assert (project_root / "pyproject.toml").exists()

def test_temp_dir_fixture(self, temp_dir):
"""Verify temp_dir fixture works."""
assert temp_dir.exists()
assert temp_dir.is_dir()

# Test writing to temp directory
test_file = temp_dir / "test.txt"
test_file.write_text("test content")
assert test_file.read_text() == "test content"

def test_sample_kitti_data_fixture(self, sample_kitti_data):
"""Verify sample KITTI data fixture."""
assert 'bbox' in sample_kitti_data
assert 'location' in sample_kitti_data
assert 'dimensions' in sample_kitti_data
assert len(sample_kitti_data['bbox']) == 4
assert len(sample_kitti_data['location']) == 3

def test_mock_opts_fixture(self, mock_opts):
"""Verify mock options fixture."""
assert hasattr(mock_opts, 'device')
assert hasattr(mock_opts, 'arch')
assert hasattr(mock_opts, 'heads')
assert mock_opts.device == 'cpu'

def test_kitti_annotation_fixture(self, kitti_annotation_line):
"""Verify KITTI annotation fixture format."""
parts = kitti_annotation_line.split()
assert len(parts) >= 14 # KITTI format has at least 14 fields
assert parts[0] == "Car" # Object type

def test_test_data_dir_fixture(self, test_data_dir):
"""Verify test data directory structure."""
assert test_data_dir.exists()
assert (test_data_dir / "images").exists()
assert (test_data_dir / "labels").exists()
assert (test_data_dir / "calib").exists()

@pytest.mark.unit
def test_unit_marker(self):
"""Test that unit marker works."""
assert True

@pytest.mark.integration
def test_integration_marker(self):
"""Test that integration marker works."""
assert True

@pytest.mark.slow
def test_slow_marker(self):
"""Test that slow marker works."""
import time
time.sleep(0.01) # Minimal delay to simulate slow test
assert True

def test_mock_logger_fixture(self, mock_logger):
"""Verify mock logger fixture."""
mock_logger.info("test message")
mock_logger.info.assert_called_with("test message")

def test_imports_work(self):
"""Test that key project modules can be imported."""
# Test numpy import (required dependency)
import numpy as np
arr = np.array([1, 2, 3])
assert len(arr) == 3

def test_pathlib_works(self, temp_dir):
"""Test pathlib integration."""
test_path = temp_dir / "subdir" / "file.txt"
test_path.parent.mkdir(parents=True, exist_ok=True)
test_path.write_text("pathlib test")
assert test_path.exists()
assert test_path.read_text() == "pathlib test"
Empty file added tests/unit/__init__.py
Empty file.