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 @@ -70,3 +70,42 @@ docs/_build/
# PyBuilder
target/

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

# Claude settings
.claude/*

# Poetry
dist/
poetry.toml

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

# IDE files
.vscode/
.idea/
*.sublime-project
*.sublime-workspace

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

101 changes: 101 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
[tool.poetry]
name = "hypernerf"
version = "0.0.1"
description = "Code for 'HyperNeRF'."
authors = ["Keunhong Park <kpar@cs.washington.edu>"]
readme = "README.md"
homepage = "https://github.com/google/hypernerf"
repository = "https://github.com/google/hypernerf"
license = "Apache-2.0"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
]
packages = [{include = "hypernerf"}]

[tool.poetry.dependencies]
python = ">=3.8,<3.12"
absl-py = ">=0.13.0"
flax = "^0.3.4"
gin-config = {git = "https://github.com/google/gin-config", rev = "243ba87b3fcfeb2efb4a920b8f19679b61a6f0dc"}
imageio = "^2.9.0"
immutabledict = "^2.2.0"
jax = ">=0.2.20,<0.5.0"
jaxlib = ">=0.1.71,<0.5.0"
Markdown = "^3.3.4"
matplotlib = "^3.4.3"
numpy = ">=1.19.2,<1.24"
oauthlib = "^3.1.1"
opencv-python = "^4.5.3.56"
opt-einsum = "^3.3.0"
optax = "^0.0.9"
Pillow = "^9.0.0"
scipy = "^1.7.1"
tensorflow = ">=2.6.3,<2.12"
tqdm = "^4.62.2"

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

[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*"]
python_functions = ["test_*"]
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"--cov=hypernerf",
"--cov-branch",
"--cov-report=term-missing:skip-covered",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=80",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]

[tool.coverage.run]
source = ["hypernerf"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/site-packages/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"raise AssertionError",
"raise NotImplementedError",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"if typing.TYPE_CHECKING:",
]
precision = 2
show_missing = true
skip_covered = true

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

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

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
36 changes: 36 additions & 0 deletions run_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Simple test runner script for when Poetry is not available."""
import subprocess
import sys
import os

def main():
"""Run pytest with configured options."""
# Add the project root to Python path
project_root = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, project_root)

# Build pytest command with options from pyproject.toml
pytest_args = [
"pytest",
"-ra",
"--strict-markers",
"--strict-config",
"--cov=hypernerf",
"--cov-branch",
"--cov-report=term-missing:skip-covered",
"--cov-report=html",
"--cov-report=xml",
"--cov-fail-under=80",
"tests"
]

# Add any additional arguments passed to the script
pytest_args.extend(sys.argv[1:])

# Run pytest
result = subprocess.run(pytest_args)
sys.exit(result.returncode)

if __name__ == "__main__":
main()
Empty file added tests/__init__.py
Empty file.
152 changes: 152 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import tempfile
from pathlib import Path
from typing import Generator

import pytest


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Provides a temporary directory that is cleaned up after the test."""
with tempfile.TemporaryDirectory() as tmp_dir:
yield Path(tmp_dir)


@pytest.fixture
def mock_config_file(temp_dir: Path) -> Path:
"""Creates a mock gin configuration file for testing."""
config_path = temp_dir / "test_config.gin"
config_content = """
# Test configuration
train.batch_size = 16
train.learning_rate = 0.001
model.num_layers = 3
"""
config_path.write_text(config_content)
return config_path


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

# Create a simple test image
img_array = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
img = Image.fromarray(img_array)

img_path = temp_dir / "test_image.png"
img.save(img_path)
return img_path


@pytest.fixture
def mock_camera_data() -> dict:
"""Provides sample camera parameters for testing."""
return {
"camera_to_world": [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0]
],
"focal_length": 50.0,
"pixel_aspect_ratio": 1.0,
"principal_point": [640.0, 360.0],
"image_size": [1280, 720],
"radial_distortion": [0.0, 0.0],
"tangential_distortion": [0.0, 0.0],
"camera_type": "PERSPECTIVE"
}


@pytest.fixture
def mock_dataset_config() -> dict:
"""Provides a mock dataset configuration."""
return {
"dataset_name": "test_dataset",
"data_dir": "/tmp/test_data",
"image_scale": 1,
"shuffle_pixels": False,
"batch_size": 16,
"num_epochs": 10,
"use_appearance_id": True,
"use_camera_id": False,
"use_warp_id": True,
"use_time_id": True,
}


@pytest.fixture(autouse=True)
def setup_test_environment():
"""Automatically set up test environment for all tests."""
import os

# Set environment variables for testing
os.environ["JAX_PLATFORM_NAME"] = "cpu"
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" # Suppress TensorFlow warnings

yield

# Cleanup if needed
pass


@pytest.fixture
def mock_jax_device():
"""Mock JAX device for testing without GPU."""
import jax

# Force CPU device for testing
return jax.devices("cpu")[0]


@pytest.fixture
def small_model_config() -> dict:
"""Provides a small model configuration suitable for testing."""
return {
"num_coarse_samples": 64,
"num_fine_samples": 128,
"use_viewdirs": True,
"use_stratified_sampling": True,
"near": 0.0,
"far": 1.0,
"noise_std": 0.0,
"net_depth": 4, # Smaller for testing
"net_width": 128, # Smaller for testing
"net_depth_condition": 1,
"net_width_condition": 64,
"skip_layer": 2,
"num_rgb_channels": 3,
"num_sigma_channels": 1,
"white_background": False,
"min_deg_point": 0,
"max_deg_point": 10,
"deg_view": 4,
"num_levels": 2,
}


@pytest.fixture
def capture_logs():
"""Fixture to capture logs during tests."""
import logging
from io import StringIO

log_capture = StringIO()
handler = logging.StreamHandler(log_capture)
handler.setLevel(logging.DEBUG)

# Get root logger
logger = logging.getLogger()
original_level = logger.level
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)

yield log_capture

# Restore original state
logger.removeHandler(handler)
logger.setLevel(original_level)
log_capture.close()
Empty file added tests/integration/__init__.py
Empty file.
Loading