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
32 changes: 32 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,35 @@ results/
thirdparty/ncore/

playground/assets/

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

# Claude settings
.claude/*

# Poetry
dist/
build/
*.egg-info/

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

# IDE files
.idea/
*.swp
*.swo
*~
.DS_Store
112 changes: 112 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
[tool.poetry]
name = "threedgrut"
version = "0.1.0"
description = "3D Gaussian Rendering and Tracing"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
packages = [
{ include = "threedgrut" },
{ include = "threedgrt_tracer" },
{ include = "threedgut_tracer" },
{ include = "threedgrut_playground" }
]

[tool.poetry.dependencies]
python = "^3.11"
torchmetrics = "*"
tensorboard = "*"
slangtorch = "1.3.4"
plyfile = "*"
polyscope = ">=2.3.0"
libigl = "*"
pygltflib = "*"
scikit-learn = "*"
wandb = "*"
fire = "*"
omegaconf = "*"
hydra-core = "*"
kornia = "*"
opencv-python = "*"
einops = "*"
imageio = "*"
msgpack = "*"
dataclasses-json = "*"
addict = "*"
rich = "*"
tqdm = "*"
setuptools = "<72.1.0"

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

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

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

[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--cov=threedgrut",
"--cov=threedgrt_tracer",
"--cov=threedgut_tracer",
"--cov=threedgrut_playground",
"--cov-report=term-missing",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=80",
"-vv"
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: marks tests as unit tests (deselect with '-m \"not unit\"')",
"integration: marks tests as integration tests (deselect with '-m \"not integration\"')",
"slow: marks tests as slow (deselect with '-m \"not slow\"')"
]
filterwarnings = [
"ignore::DeprecationWarning",
"ignore::PendingDeprecationWarning"
]

[tool.coverage.run]
source = ["threedgrut", "threedgrt_tracer", "threedgut_tracer", "threedgrut_playground"]
omit = [
"*/tests/*",
"*/__pycache__/*",
"*/site-packages/*",
"*/setup*.py",
"*/conftest.py"
]

[tool.coverage.report]
precision = 2
show_missing = true
skip_covered = false
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"
]

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

[tool.coverage.xml]
output = "coverage.xml"
Empty file added tests/__init__.py
Empty file.
207 changes: 207 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
"""Shared pytest fixtures and configuration for the threedgrut test suite."""
import os
import tempfile
from pathlib import Path
from typing import Generator, Any
import shutil

import pytest
from omegaconf import DictConfig, OmegaConf


@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 temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file within the temp directory."""
temp_path = temp_dir / "test_file.txt"
temp_path.write_text("test content")
yield temp_path


@pytest.fixture
def sample_config() -> DictConfig:
"""Create a sample configuration for testing."""
config = {
"model": {
"type": "gaussian",
"num_points": 1000,
"learning_rate": 0.001,
},
"dataset": {
"type": "nerf",
"path": "/path/to/dataset",
"batch_size": 32,
},
"training": {
"max_iterations": 10000,
"checkpoint_interval": 1000,
"validation_interval": 500,
},
"render": {
"resolution": [800, 600],
"samples_per_pixel": 1,
}
}
return OmegaConf.create(config)


@pytest.fixture
def mock_dataset_path(temp_dir: Path) -> Path:
"""Create a mock dataset directory structure."""
dataset_dir = temp_dir / "mock_dataset"
dataset_dir.mkdir()

# Create subdirectories
(dataset_dir / "images").mkdir()
(dataset_dir / "sparse").mkdir()
(dataset_dir / "dense").mkdir()

# Create some mock files
(dataset_dir / "images" / "image_001.jpg").touch()
(dataset_dir / "images" / "image_002.jpg").touch()
(dataset_dir / "sparse" / "cameras.bin").touch()
(dataset_dir / "sparse" / "images.bin").touch()
(dataset_dir / "sparse" / "points3D.bin").touch()

return dataset_dir


@pytest.fixture
def mock_checkpoint_path(temp_dir: Path) -> Path:
"""Create a mock checkpoint file."""
checkpoint_path = temp_dir / "checkpoint.ckpt"
checkpoint_path.write_text("mock checkpoint data")
return checkpoint_path


@pytest.fixture
def mock_ply_file(temp_dir: Path) -> Path:
"""Create a mock PLY file for testing."""
ply_path = temp_dir / "pointcloud.ply"
ply_content = """ply
format ascii 1.0
element vertex 3
property float x
property float y
property float z
end_header
0.0 0.0 0.0
1.0 0.0 0.0
0.0 1.0 0.0
"""
ply_path.write_text(ply_content)
return ply_path


@pytest.fixture
def mock_camera_params() -> dict[str, Any]:
"""Create mock camera parameters."""
return {
"width": 800,
"height": 600,
"fx": 500.0,
"fy": 500.0,
"cx": 400.0,
"cy": 300.0,
"k1": 0.0,
"k2": 0.0,
"p1": 0.0,
"p2": 0.0,
}


@pytest.fixture
def mock_render_params() -> dict[str, Any]:
"""Create mock render parameters."""
return {
"resolution": [800, 600],
"samples_per_pixel": 1,
"background_color": [0.0, 0.0, 0.0],
"near_plane": 0.1,
"far_plane": 100.0,
}


@pytest.fixture
def clean_environment(monkeypatch):
"""Clean environment variables for testing."""
# Remove any existing CUDA-related environment variables
cuda_vars = ["CUDA_VISIBLE_DEVICES", "CUDA_DEVICE_ORDER"]
for var in cuda_vars:
monkeypatch.delenv(var, raising=False)

# Set testing environment
monkeypatch.setenv("TESTING", "1")
monkeypatch.setenv("HYDRA_FULL_ERROR", "1")


@pytest.fixture
def mock_wandb(monkeypatch):
"""Mock wandb to prevent actual logging during tests."""
monkeypatch.setenv("WANDB_MODE", "disabled")
monkeypatch.setenv("WANDB_SILENT", "true")


@pytest.fixture(autouse=True)
def change_test_dir(request, monkeypatch, temp_dir):
"""Automatically change to temp directory for each test."""
monkeypatch.chdir(temp_dir)
yield
# No need to change back as monkeypatch handles cleanup


@pytest.fixture
def sample_point_cloud() -> dict[str, list[float]]:
"""Create a sample point cloud data."""
return {
"positions": [
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
],
"colors": [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[1.0, 1.0, 0.0],
],
"scales": [
[0.1, 0.1, 0.1],
[0.1, 0.1, 0.1],
[0.1, 0.1, 0.1],
[0.1, 0.1, 0.1],
],
}


@pytest.fixture
def gpu_available() -> bool:
"""Check if GPU is available for testing."""
try:
import torch
return torch.cuda.is_available()
except ImportError:
return False


@pytest.fixture
def skip_if_no_gpu(gpu_available):
"""Skip test if GPU is not available."""
if not gpu_available:
pytest.skip("GPU not available")


@pytest.fixture
def mock_timer(mocker):
"""Mock timer for performance testing."""
timer = mocker.MagicMock()
timer.elapsed_time = 0.1
timer.average_time = 0.1
return timer
Empty file added tests/integration/__init__.py
Empty file.
Loading