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
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,31 @@ dmypy.json
.prof

# End of https://www.toptal.com/developers/gitignore/api/python

# Claude Code specific
.claude/*

# Additional testing artifacts
.coverage.*
pytest_cache/
test-results/
.pytest_cache/

# IDE and editor files
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Virtual environments
.venv/
venv/

# Build artifacts
*.pyd
*.so
dist/
build/
*.egg-info/
3,494 changes: 3,494 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

91 changes: 91 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
[tool.poetry]
name = "segm"
version = "0.0.1"
description = "Segmenter: Transformer for Semantic Segmentation"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
license = "MIT"
packages = [{include = "segm"}]

[tool.poetry.dependencies]
python = "^3.8"
torch = "*"
click = "*"
numpy = "*"
einops = "*"
python-hostlist = "*"
tqdm = "*"
requests = "*"
pyyaml = "*"
timm = "0.4.12"
mmcv = "1.3.8"
mmsegmentation = "0.14.1"

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

[tool.poetry.scripts]
test = "pytest:main"
tests = "pytest:main"

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-v",
"--strict-markers",
"--tb=short",
"--cov=segm",
"--cov-report=term-missing",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=80",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow tests",
]
filterwarnings = [
"ignore::DeprecationWarning",
"ignore::PendingDeprecationWarning",
]

[tool.coverage.run]
source = ["segm"]
omit = [
"*/tests/*",
"*/test_*",
"*/__init__.py",
"*/setup.py",
"*/scripts/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if __name__ == .__main__.:",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if False:",
"pass",
]
precision = 2
show_missing = true

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

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
3 changes: 3 additions & 0 deletions segm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Segmenter: Transformer for Semantic Segmentation."""

__version__ = "0.0.1"
Empty file added tests/__init__.py
Empty file.
193 changes: 193 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import os
import tempfile
import shutil
from pathlib import Path
from unittest.mock import MagicMock

import pytest
import torch
import yaml


@pytest.fixture
def temp_dir():
"""Create a temporary directory for test files."""
temp_dir = tempfile.mkdtemp()
yield Path(temp_dir)
shutil.rmtree(temp_dir)


@pytest.fixture
def mock_config():
"""Create a mock configuration dictionary."""
return {
"dataset": "ade20k",
"backbone": "vit_small_patch16_224",
"decoder": "mask_transformer",
"optimizer": "sgd",
"scheduler": "polynomial",
"batch_size": 16,
"epochs": 100,
"learning_rate": 0.01,
"image_size": [512, 512],
"window_size": 512,
"window_stride": 512,
"n_cls": 150,
"d_encoder": 384,
"n_layers": 2,
"n_heads": 6,
"d_model": 384,
"d_ff": 1536,
"drop_path_rate": 0.0,
"dropout": 0.1,
}


@pytest.fixture
def mock_yaml_config(temp_dir, mock_config):
"""Create a temporary YAML config file."""
config_path = temp_dir / "test_config.yaml"
with open(config_path, "w") as f:
yaml.dump(mock_config, f)
return config_path


@pytest.fixture
def mock_model():
"""Create a mock model with common methods."""
model = MagicMock()
model.eval = MagicMock(return_value=model)
model.train = MagicMock(return_value=model)
model.to = MagicMock(return_value=model)
model.state_dict = MagicMock(return_value={})
model.load_state_dict = MagicMock()
model.parameters = MagicMock(return_value=[torch.randn(10, 10)])
return model


@pytest.fixture
def mock_dataset():
"""Create a mock dataset."""
dataset = MagicMock()
dataset.__len__ = MagicMock(return_value=100)
dataset.__getitem__ = MagicMock(return_value=(
torch.randn(3, 224, 224), # image
torch.randint(0, 150, (224, 224)) # mask
))
return dataset


@pytest.fixture
def mock_dataloader(mock_dataset):
"""Create a mock dataloader."""
dataloader = MagicMock()
dataloader.__iter__ = MagicMock(return_value=iter([
(torch.randn(4, 3, 224, 224), torch.randint(0, 150, (4, 224, 224)))
for _ in range(5)
]))
dataloader.__len__ = MagicMock(return_value=5)
dataloader.dataset = mock_dataset
return dataloader


@pytest.fixture
def mock_optimizer():
"""Create a mock optimizer."""
optimizer = MagicMock()
optimizer.zero_grad = MagicMock()
optimizer.step = MagicMock()
optimizer.state_dict = MagicMock(return_value={})
optimizer.load_state_dict = MagicMock()
return optimizer


@pytest.fixture
def mock_scheduler():
"""Create a mock learning rate scheduler."""
scheduler = MagicMock()
scheduler.step = MagicMock()
scheduler.state_dict = MagicMock(return_value={})
scheduler.load_state_dict = MagicMock()
scheduler.get_last_lr = MagicMock(return_value=[0.01])
return scheduler


@pytest.fixture
def sample_image():
"""Create a sample image tensor."""
return torch.randn(1, 3, 512, 512)


@pytest.fixture
def sample_mask():
"""Create a sample segmentation mask."""
return torch.randint(0, 150, (1, 512, 512))


@pytest.fixture
def device():
"""Get the device to use for testing."""
return torch.device("cuda" if torch.cuda.is_available() else "cpu")


@pytest.fixture(autouse=True)
def reset_torch_seed():
"""Reset PyTorch random seed for reproducible tests."""
torch.manual_seed(42)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(42)


@pytest.fixture
def mock_checkpoint(temp_dir):
"""Create a mock checkpoint file."""
checkpoint_path = temp_dir / "checkpoint.pth"
checkpoint = {
"epoch": 10,
"state_dict": {"layer1.weight": torch.randn(10, 10)},
"optimizer": {"param_groups": [{"lr": 0.01}]},
"scheduler": {"last_epoch": 10},
"best_miou": 0.75,
}
torch.save(checkpoint, checkpoint_path)
return checkpoint_path


@pytest.fixture
def mock_logger():
"""Create a mock logger."""
logger = MagicMock()
logger.info = MagicMock()
logger.debug = MagicMock()
logger.warning = MagicMock()
logger.error = MagicMock()
logger.critical = MagicMock()
return logger


@pytest.fixture
def capture_stdout(monkeypatch):
"""Capture stdout for testing print statements."""
captured = []

def mock_print(*args, **kwargs):
captured.append(" ".join(str(arg) for arg in args))

monkeypatch.setattr("builtins.print", mock_print)
return captured


@pytest.fixture
def env_vars(monkeypatch):
"""Set and restore environment variables for testing."""
original_env = dict(os.environ)

def set_env(**kwargs):
for key, value in kwargs.items():
monkeypatch.setenv(key, value)

yield set_env

# Restore original environment
os.environ.clear()
os.environ.update(original_env)
Empty file added tests/integration/__init__.py
Empty file.
Loading