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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,9 @@ venv.bak/

# mypy
.mypy_cache/

# Claude
.claude/*

# Poetry
poetry.lock
97 changes: 97 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
[tool.poetry]
name = "pdvc"
version = "0.1.0"
description = "Parallel Decoding for Video Captioning"
authors = ["PDVC Team"]
readme = "README.md"
packages = [{include = "pdvc"}, {include = "video_backbone"}, {include = "data"}, {include = "misc"}, {include = "densevid_eval3"}, {include = "visualization"}]

[tool.poetry.dependencies]
python = "^3.8"
h5py = "*"
matplotlib = "*"
numpy = "*"
pandas = "*"
Pillow = "*"
PyYAML = "*"
six = "*"
tqdm = "*"
tensorboardX = "*"
colorlog = "*"
scipy = "*"
notebook = "*"
av = "*"
joblib = "*"
google-trans-new = "*"
torch = "*"

[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 = [
"--strict-markers",
"--strict-config",
"--verbose",
"--cov=pdvc",
"--cov=video_backbone",
"--cov=data",
"--cov=misc",
"--cov=densevid_eval3",
"--cov=visualization",
"--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 tests",
]

[tool.coverage.run]
source = ["pdvc", "video_backbone", "data", "misc", "densevid_eval3", "visualization"]
omit = [
"*/tests/*",
"*/test_*.py",
"*/__pycache__/*",
"*/site-packages/*",
"setup.py",
"*/pdvc/ops/src/*",
"*/pdvc/ops/test.py",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if __name__ == .__main__.:",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if False:",
"pass",
]
precision = 2
show_missing = true

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

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Empty file added tests/__init__.py
Empty file.
164 changes: 164 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import os
import tempfile
import shutil
from pathlib import Path
from unittest.mock import Mock

import pytest
import torch
import numpy as np
import yaml


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


@pytest.fixture
def mock_config():
"""Create a mock configuration dictionary for testing."""
return {
'train': {
'batch_size': 32,
'learning_rate': 1e-4,
'num_epochs': 10,
'grad_clip': 1.0,
'weight_decay': 1e-4,
},
'model': {
'visual_feature_dim': 2048,
'hidden_dim': 512,
'num_heads': 8,
'num_layers': 6,
'dropout': 0.1,
},
'data': {
'num_workers': 4,
'max_caption_len': 50,
'video_feature_path': '/data/features',
},
'eval': {
'beam_size': 5,
'max_pred_length': 50,
}
}


@pytest.fixture
def sample_video_features():
"""Create sample video features for testing."""
batch_size = 2
num_frames = 10
feature_dim = 2048

features = torch.randn(batch_size, num_frames, feature_dim)
mask = torch.ones(batch_size, num_frames, dtype=torch.bool)

return {
'features': features,
'mask': mask,
'video_ids': ['video_001', 'video_002'],
}


@pytest.fixture
def sample_captions():
"""Create sample captions for testing."""
return [
{
'video_id': 'video_001',
'captions': [
{'sentence': 'A person walks into the room', 'timestamp': [0.0, 3.5]},
{'sentence': 'They sit down on a chair', 'timestamp': [3.5, 7.0]},
]
},
{
'video_id': 'video_002',
'captions': [
{'sentence': 'Someone opens a door', 'timestamp': [0.0, 2.0]},
{'sentence': 'They turn on the lights', 'timestamp': [2.0, 4.5]},
]
}
]


@pytest.fixture
def mock_model():
"""Create a mock model for testing."""
model = Mock()
model.train = Mock(return_value=None)
model.eval = Mock(return_value=None)
model.parameters = Mock(return_value=[torch.randn(10, 10)])
model.forward = Mock(return_value={
'loss': torch.tensor(1.0),
'predictions': torch.randn(2, 10, 100)
})
return model


@pytest.fixture
def mock_dataloader():
"""Create a mock dataloader for testing."""
dataloader = Mock()
dataloader.__iter__ = Mock(return_value=iter([
{
'video_features': torch.randn(2, 10, 2048),
'captions': ['caption1', 'caption2'],
'video_ids': ['vid1', 'vid2']
}
]))
dataloader.__len__ = Mock(return_value=10)
return dataloader


@pytest.fixture
def sample_yaml_config(temp_dir):
"""Create a sample YAML configuration file."""
config_path = temp_dir / 'config.yaml'
config = {
'experiment_name': 'test_experiment',
'model_type': 'pdvc',
'dataset': 'activitynet',
'checkpoint_dir': str(temp_dir / 'checkpoints'),
'log_dir': str(temp_dir / 'logs'),
}

with open(config_path, 'w') as f:
yaml.dump(config, f)

return config_path


@pytest.fixture
def mock_h5_file(temp_dir):
"""Create a mock HDF5 file structure for testing."""
import h5py

h5_path = temp_dir / 'features.h5'
with h5py.File(h5_path, 'w') as f:
# Add some sample video features
for i in range(5):
video_id = f'video_{i:03d}'
features = np.random.randn(20, 2048).astype(np.float32)
f.create_dataset(video_id, data=features)

return h5_path


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


@pytest.fixture(autouse=True)
def reset_random_seeds():
"""Reset random seeds before each test for reproducibility."""
np.random.seed(42)
torch.manual_seed(42)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(42)
Empty file added tests/integration/__init__.py
Empty file.
Loading