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
39 changes: 39 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,42 @@ target/

#Idea
.idea*

# Testing
.pytest_cache/
*.pytest_cache/
.coverage
.coverage.*
htmlcov/
coverage.xml
*.cover
.hypothesis/
.tox/

# Claude
.claude/*

# Virtual environments
venv/
virtualenv/
ENV/
env.bak/
venv.bak/

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

# Build artifacts
build/
dist/
*.egg-info/
.eggs/

# Temporary files
*.tmp
*.bak
.temp/
1,748 changes: 1,748 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
[tool.poetry]
name = "pytorch-swa-gaussian"
version = "0.1.0"
description = "PyTorch implementation of SWA-Gaussian for deep learning"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
packages = [{include = "models"}, {include = "utils.py"}, {include = "train.py"}]

[tool.poetry.dependencies]
python = "^3.8"
torch = ">=1.9.0"
torchvision = ">=0.10.0"
tabulate = ">=0.8.0"

[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]
minversion = "7.0"
addopts = [
"-ra",
"--strict-markers",
"--cov=models",
"--cov=utils",
"--cov=train",
"--cov-branch",
"--cov-report=term-missing:skip-covered",
"--cov-report=html:htmlcov",
"--cov-report=xml:coverage.xml",
"--cov-fail-under=20",
"--maxfail=1",
"--tb=short",
"--pythonwarnings=error",
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Tests that take > 5 seconds to run",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["models", "utils", "train"]
branch = true
omit = [
"*/tests/*",
"*/__pycache__/*",
"*/site-packages/*",
"*/.tox/*",
"*/venv/*",
"*/virtualenv/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"def __str__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"if typing.TYPE_CHECKING:",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = true
fail_under = 20

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

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

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
Empty file added tests/__init__.py
Empty file.
165 changes: 165 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
"""Shared pytest fixtures and configuration for all tests."""

import os
import sys
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock, MagicMock

import pytest
import torch
import torch.nn as nn


# Add the project root to the Python path
sys.path.insert(0, str(Path(__file__).parent.parent))


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


@pytest.fixture
def mock_model():
"""Create a mock PyTorch model for testing."""
model = Mock(spec=nn.Module)
model.parameters = MagicMock(return_value=[torch.randn(10, 10)])
model.state_dict = MagicMock(return_value={'layer1.weight': torch.randn(10, 10)})
model.eval = MagicMock()
model.train = MagicMock()
model.to = MagicMock(return_value=model)
return model


@pytest.fixture
def mock_optimizer():
"""Create a mock optimizer for testing."""
optimizer = Mock(spec=torch.optim.SGD)
optimizer.state_dict = MagicMock(return_value={
'state': {},
'param_groups': [{'lr': 0.1, 'momentum': 0.9, 'weight_decay': 1e-4}]
})
optimizer.load_state_dict = MagicMock()
optimizer.step = MagicMock()
optimizer.zero_grad = MagicMock()
return optimizer


@pytest.fixture
def mock_dataloader():
"""Create a mock DataLoader for testing."""
batch_size = 32
num_batches = 10

data = []
for _ in range(num_batches):
inputs = torch.randn(batch_size, 3, 32, 32)
targets = torch.randint(0, 10, (batch_size,))
data.append((inputs, targets))

dataloader = Mock()
dataloader.__iter__ = Mock(return_value=iter(data))
dataloader.__len__ = Mock(return_value=num_batches)
dataloader.batch_size = batch_size
return dataloader


@pytest.fixture
def sample_checkpoint_data():
"""Create sample checkpoint data for testing."""
return {
'epoch': 10,
'state_dict': {'layer1.weight': torch.randn(10, 10)},
'optimizer': {
'state': {},
'param_groups': [{'lr': 0.1, 'momentum': 0.9, 'weight_decay': 1e-4}]
},
'test_acc': 85.5,
'train_loss': 0.123
}


@pytest.fixture
def mock_args():
"""Create mock command line arguments for testing."""
args = Mock()
args.dir = '/tmp/test_dir'
args.dataset = 'CIFAR10'
args.data_path = '/tmp/data'
args.batch_size = 128
args.num_workers = 4
args.model = 'PreResNet110'
args.resume = None
args.epochs = 200
args.save_freq = 25
args.eval_freq = 5
args.lr_init = 0.1
args.momentum = 0.9
args.wd = 1e-4
args.swa = False
args.swa_start = 161
args.swa_lr = 0.05
args.swa_c_epochs = 1
args.seed = 1
return args


@pytest.fixture
def cuda_available():
"""Check if CUDA is available and skip test if not."""
if not torch.cuda.is_available():
pytest.skip("CUDA not available")
return True


@pytest.fixture
def small_model():
"""Create a small neural network for testing."""
class SmallNet(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(10, 5)
self.fc2 = nn.Linear(5, 2)

def forward(self, x):
x = torch.relu(self.fc1(x))
return self.fc2(x)

return SmallNet()


@pytest.fixture(autouse=True)
def reset_random_seeds():
"""Reset random seeds before each test for reproducibility."""
torch.manual_seed(42)
torch.cuda.manual_seed_all(42)
if torch.cuda.is_available():
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False


@pytest.fixture
def capture_stdout(monkeypatch):
"""Capture stdout for testing print statements."""
import io
captured_output = io.StringIO()
monkeypatch.setattr('sys.stdout', captured_output)
return captured_output


@pytest.fixture
def mock_time(monkeypatch):
"""Mock time.time() for consistent time measurements."""
current_time = [0.0]

def mock_time_func():
current_time[0] += 1.0
return current_time[0]

monkeypatch.setattr('time.time', mock_time_func)
return current_time
Empty file added tests/integration/__init__.py
Empty file.
Loading