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
44 changes: 44 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,47 @@ cache
pretrained_enc_ckpts
vqgan_jax_weakaug.ckpt
imagenet-val

# Testing and coverage
.pytest_cache/
.coverage
htmlcov/
coverage.xml
*.cover
*.py,cover
.hypothesis/
.tox/
.nox/

# Claude settings
.claude/*

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

# Build artifacts
build/
dist/
*.egg
*.whl

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

# OS files
.DS_Store
Thumbs.db

# Logs
*.log

# mypy
.mypy_cache/
.dmypy.json
dmypy.json
8,109 changes: 8,109 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

161 changes: 161 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
[tool.poetry]
name = "rcg"
version = "0.0.1"
description = "Representation-Conditioned image Generation"
authors = ["RCG Team"]
readme = "README.md"
license = "MIT"
packages = [{include = "*", from = "."}]

[tool.poetry.dependencies]
python = "^3.8.5"
torch = ">=1.7.1"
torchvision = ">=0.8.2"
numpy = ">=1.19.2"
albumentations = ">=0.4.3"
opencv-python = ">=4.1.2.30"
imageio = ">=2.9.0"
imageio-ffmpeg = ">=0.4.2"
pytorch-lightning = ">=1.4.2"
torchmetrics = ">=0.5.0"
omegaconf = ">=2.1.1"
test-tube = ">=0.7.5"
streamlit = ">=0.73.1"
einops = ">=0.3.0"
more-itertools = ">=8.0.0"
timm = ">=0.3.2"
protobuf = ">=3.20"
submitit = ">=1.3.3"
diffusers = ">=0.7.0"
accelerate = ">=0.10.0"
huggingface-hub = ">=0.21.4"
transformers = ">=4.18.0"
tqdm = "*"
setuptools = ">=59.5.0"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
pytest-cov = "^4.1.0"
pytest-mock = "^3.11.0"
pytest-xdist = "^3.3.0"
pytest-timeout = "^2.1.0"
black = "^23.0.0"
isort = "^5.12.0"
flake8 = "^6.0.0"
mypy = "^1.4.0"
pudb = ">=2019.2"

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

[tool.pytest.ini_options]
minversion = "7.0"
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*", "*Tests"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--strict-config",
"--verbose",
"--tb=short",
"--cov=.",
"--cov-report=term-missing:skip-covered",
"--cov-report=html:htmlcov",
"--cov-report=xml:coverage.xml",
# Coverage threshold disabled for initial setup - enable when adding actual tests
# "--cov-fail-under=80",
"-p no:warnings",
]
markers = [
"unit: marks tests as unit tests (fast, isolated)",
"integration: marks tests as integration tests (may require external resources)",
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
]

[tool.coverage.run]
source = ["."]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/site-packages/*",
"setup.py",
"*/migrations/*",
"*/.venv/*",
"*/venv/*",
"*/env/*",
"*/.tox/*",
"*/.eggs/*",
"*/dist/*",
"*/build/*",
"*/.git/*",
"*/__init__.py",
"*/conftest.py",
]

[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:",
"@abstractmethod",
"@abc.abstractmethod",
]
precision = 2
show_missing = true
skip_covered = false

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

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

[tool.isort]
profile = "black"
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true
line_length = 88
skip_gitignore = true

[tool.black]
line-length = 88
target-version = ['py38']
include = '\.pyi?$'
extend-exclude = '''
(
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
)
'''

[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = false
ignore_missing_imports = true

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

import os
import tempfile
from pathlib import Path
from typing import Generator, Dict, Any

import pytest
import torch
import numpy as np
from omegaconf import OmegaConf, DictConfig


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
with tempfile.TemporaryDirectory() as tmp_dir:
yield Path(tmp_dir)


@pytest.fixture
def mock_config() -> DictConfig:
"""Provide a mock configuration for testing."""
config = {
"model": {
"name": "test_model",
"hidden_dim": 128,
"num_layers": 2,
"dropout": 0.1,
},
"training": {
"batch_size": 32,
"learning_rate": 1e-4,
"num_epochs": 10,
"seed": 42,
},
"data": {
"dataset": "test_dataset",
"num_workers": 2,
"pin_memory": True,
},
"logging": {
"log_dir": "logs",
"save_interval": 100,
"val_interval": 50,
},
}
return OmegaConf.create(config)


@pytest.fixture
def sample_tensor() -> torch.Tensor:
"""Provide a sample tensor for testing."""
return torch.randn(4, 3, 256, 256)


@pytest.fixture
def sample_batch() -> Dict[str, torch.Tensor]:
"""Provide a sample batch of data for testing."""
batch_size = 4
return {
"images": torch.randn(batch_size, 3, 256, 256),
"labels": torch.randint(0, 10, (batch_size,)),
"masks": torch.ones(batch_size, 1, 256, 256),
}


@pytest.fixture
def numpy_random_state():
"""Fixture to ensure reproducible numpy random state."""
original_state = np.random.get_state()
np.random.seed(42)
yield
np.random.set_state(original_state)


@pytest.fixture
def torch_random_state():
"""Fixture to ensure reproducible torch random state."""
original_state = torch.get_rng_state()
torch.manual_seed(42)
if torch.cuda.is_available():
original_cuda_state = torch.cuda.get_rng_state()
torch.cuda.manual_seed(42)
yield
torch.set_rng_state(original_state)
if torch.cuda.is_available():
torch.cuda.set_rng_state(original_cuda_state)


@pytest.fixture
def mock_model_checkpoint(temp_dir: Path) -> Path:
"""Create a mock model checkpoint file."""
checkpoint_path = temp_dir / "model_checkpoint.pt"
checkpoint = {
"epoch": 10,
"model_state_dict": {"layer1.weight": torch.randn(10, 10)},
"optimizer_state_dict": {"param_groups": [{"lr": 0.001}]},
"loss": 0.5,
}
torch.save(checkpoint, checkpoint_path)
return checkpoint_path


@pytest.fixture
def mock_image_file(temp_dir: Path) -> Path:
"""Create a mock image file for testing."""
import numpy as np
from PIL import Image

image_path = temp_dir / "test_image.png"
image_array = np.random.randint(0, 255, (256, 256, 3), dtype=np.uint8)
image = Image.fromarray(image_array)
image.save(image_path)
return image_path


@pytest.fixture
def device() -> torch.device:
"""Return the appropriate device for testing."""
return torch.device("cuda" if torch.cuda.is_available() else "cpu")


@pytest.fixture(autouse=True)
def cleanup_cuda():
"""Automatically clean up CUDA cache after each test."""
yield
if torch.cuda.is_available():
torch.cuda.empty_cache()


@pytest.fixture
def mock_environment_variables() -> Generator[Dict[str, str], None, None]:
"""Temporarily set environment variables for testing."""
original_env = os.environ.copy()
test_env = {
"CUDA_VISIBLE_DEVICES": "0",
"OMP_NUM_THREADS": "1",
"PYTHONPATH": str(Path(__file__).parent.parent),
}
os.environ.update(test_env)
yield test_env
os.environ.clear()
os.environ.update(original_env)


@pytest.fixture
def capture_logs(caplog):
"""Fixture to capture and assert on log messages."""
with caplog.at_level("DEBUG"):
yield caplog


def pytest_configure(config):
"""Configure pytest with custom markers and settings."""
config.addinivalue_line(
"markers", "gpu: marks tests that require GPU (deselect with '-m \"not gpu\"')"
)
config.addinivalue_line(
"markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')"
)
config.addinivalue_line(
"markers", "unit: marks tests as unit tests"
)
config.addinivalue_line(
"markers", "integration: marks tests as integration tests"
)


def pytest_collection_modifyitems(config, items):
"""Modify test collection to add markers based on test location."""
for item in items:
# Add markers based on test file location
if "unit" in str(item.fspath):
item.add_marker(pytest.mark.unit)
elif "integration" in str(item.fspath):
item.add_marker(pytest.mark.integration)

# Skip GPU tests if CUDA is not available
if "gpu" in item.keywords and not torch.cuda.is_available():
skip_gpu = pytest.mark.skip(reason="GPU not available")
item.add_marker(skip_gpu)
Empty file added tests/integration/__init__.py
Empty file.
Loading