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
151 changes: 151 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
*.manifest
*.spec

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
Pipfile.lock

# poetry
#poetry.lock

# pdm
.pdm.toml

# PEP 582
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyTorch
*.pth
*.pt

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

# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Claude Code
.claude/*
1,152 changes: 1,152 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

77 changes: 77 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
[tool.poetry]
name = "deep-supervision"
version = "0.1.0"
description = "Deep Supervision with Intermediate Classifiers"
authors = ["Your Name <your.email@example.com>"]
readme = "README.md"
packages = [{include = "cifar"}, {include = "fine-grained", from = "."}]

[tool.poetry.dependencies]
python = "^3.8"
torch = "^2.0.0"
torchvision = "^0.15.0"
numpy = "^1.21.0"
pillow = "^9.0.0"

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


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

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--strict-config",
"--verbose",
"--cov=cifar",
"--cov=fine-grained",
"--cov-report=term-missing",
"--cov-report=html:htmlcov",
"--cov-report=xml:coverage.xml",
"--cov-fail-under=80"
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests"
]

[tool.coverage.run]
source = ["cifar", "fine-grained"]
omit = [
"tests/*",
"*/test_*",
"*/__pycache__/*",
"*/migrations/*",
"*/venv/*",
"*/.venv/*"
]

[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__.:"
]
show_missing = true
fail_under = 80

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

[tool.coverage.xml]
output = "coverage.xml"
Empty file added tests/__init__.py
Empty file.
156 changes: 156 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import os
import tempfile
import pytest
import torch
import numpy as np
from pathlib import Path


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


@pytest.fixture
def temp_file(temp_dir):
"""Create a temporary file in the temp directory."""
temp_file = temp_dir / "test_file.txt"
temp_file.write_text("test content")
return temp_file


@pytest.fixture
def sample_tensor():
"""Create a sample PyTorch tensor for testing."""
return torch.randn(32, 3, 224, 224)


@pytest.fixture
def sample_labels():
"""Create sample labels tensor."""
return torch.randint(0, 10, (32,))


@pytest.fixture
def mock_dataset_config():
"""Mock configuration for dataset testing."""
return {
'batch_size': 32,
'num_workers': 0,
'shuffle': True,
'pin_memory': False
}


@pytest.fixture
def mock_model_config():
"""Mock configuration for model testing."""
return {
'num_classes': 10,
'arch': 'resnet18',
'pretrained': False,
'dropout': 0.5
}


@pytest.fixture
def mock_training_config():
"""Mock configuration for training testing."""
return {
'epochs': 1,
'lr': 0.01,
'momentum': 0.9,
'weight_decay': 1e-4,
'print_freq': 10
}


@pytest.fixture
def small_image_batch():
"""Create a small batch of images for testing."""
return torch.randn(4, 3, 32, 32)


@pytest.fixture
def cifar_like_batch():
"""Create a CIFAR-10 like batch for testing."""
return {
'images': torch.randn(16, 3, 32, 32),
'labels': torch.randint(0, 10, (16,))
}


@pytest.fixture
def imagenet_like_batch():
"""Create an ImageNet-like batch for testing."""
return {
'images': torch.randn(8, 3, 224, 224),
'labels': torch.randint(0, 1000, (8,))
}


@pytest.fixture(autouse=True)
def set_deterministic():
"""Set deterministic behavior for reproducible tests."""
torch.manual_seed(42)
np.random.seed(42)
if torch.cuda.is_available():
torch.cuda.manual_seed(42)
torch.cuda.manual_seed_all(42)

# Set deterministic algorithms
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False


@pytest.fixture
def device():
"""Get the device for testing (CPU by default)."""
return torch.device('cpu')


@pytest.fixture
def mock_checkpoint_data():
"""Create mock checkpoint data."""
return {
'epoch': 10,
'state_dict': {'layer.weight': torch.randn(10, 5)},
'best_acc': 0.85,
'optimizer': {'state': {}, 'param_groups': [{'lr': 0.01}]},
'arch': 'resnet18'
}


@pytest.fixture
def sample_image_paths(temp_dir):
"""Create sample image file paths."""
image_dir = temp_dir / "images"
image_dir.mkdir()

paths = []
for i in range(5):
path = image_dir / f"image_{i}.jpg"
# Create empty files to simulate images
path.write_bytes(b"fake_image_data")
paths.append(str(path))

return paths


@pytest.fixture
def mock_loss_history():
"""Create mock loss history for training tests."""
return {
'train_loss': [1.5, 1.2, 1.0, 0.8, 0.6],
'val_loss': [1.4, 1.3, 1.1, 0.9, 0.7],
'train_acc': [0.4, 0.5, 0.6, 0.7, 0.8],
'val_acc': [0.45, 0.52, 0.62, 0.72, 0.82]
}


@pytest.fixture(scope="session")
def test_data_root(tmp_path_factory):
"""Create a session-scoped temporary directory for test data."""
return tmp_path_factory.mktemp("test_data")
Empty file added tests/integration/__init__.py
Empty file.
Loading