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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,6 @@ logs/
wandb/
.vscode/
*.pdf

# Claude Code settings
.claude/*
282 changes: 282 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

77 changes: 77 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
[tool.poetry]
name = "beso"
version = "0.1.0"
description = "Code for the RSS paper Goal Conditioned Imitation Learning using Score-based Diffusion Policies"
license = "MIT"
authors = ["Moritz Reuss"]
readme = "README.md"
packages = [{include = "beso"}]

[tool.poetry.dependencies]
python = "^3.8"

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

[tool.poetry.scripts]

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"--strict-markers",
"--strict-config",
"--cov=beso",
"--cov-report=html:htmlcov",
"--cov-report=xml:coverage.xml",
"--cov-report=term-missing",
"--cov-fail-under=80",
"-v",
"--tb=short"
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests"
]

[tool.coverage.run]
source = ["beso"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/env/*",
"setup.py"
]

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

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

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

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Empty file added tests/__init__.py
Empty file.
126 changes: 126 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""Shared pytest fixtures for the BESO test suite."""

import tempfile
import shutil
from pathlib import Path
from typing import Dict, Any
from unittest.mock import MagicMock, patch

import pytest


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


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary for testing."""
return {
"model": {
"name": "test_model",
"hidden_dim": 128,
"num_layers": 4
},
"training": {
"batch_size": 32,
"learning_rate": 0.001,
"epochs": 10
},
"env": {
"name": "block_pushing",
"action_dim": 4,
"obs_dim": 10
}
}


@pytest.fixture
def mock_observation():
"""Provide a mock observation for testing agents and environments."""
return {
"state": [0.1, 0.2, 0.3, 0.4, 0.5],
"image": None, # Could be replaced with actual image data in specific tests
"goal": [0.8, 0.9, 1.0]
}


@pytest.fixture
def mock_action():
"""Provide a mock action for testing."""
return [0.5, -0.2, 0.8, 0.1]


@pytest.fixture
def mock_trajectory():
"""Provide a mock trajectory for testing data loaders."""
return {
"observations": [[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5]],
"actions": [[0.5, -0.2], [0.6, -0.1], [0.7, 0.0]],
"rewards": [0.1, 0.2, 0.5],
"dones": [False, False, True]
}


@pytest.fixture
def mock_device():
"""Mock device for testing (CPU)."""
return "cpu"


@pytest.fixture
def mock_torch_model():
"""Create a mock PyTorch model for testing."""
model = MagicMock()
model.train.return_value = None
model.eval.return_value = None
model.parameters.return_value = []
return model


@pytest.fixture
def mock_pybullet():
"""Mock pybullet for environment testing without actual physics simulation."""
with patch('pybullet') as mock_pb:
mock_pb.connect.return_value = 0
mock_pb.disconnect.return_value = None
mock_pb.loadURDF.return_value = 1
mock_pb.resetBasePositionAndOrientation.return_value = None
mock_pb.stepSimulation.return_value = None
yield mock_pb


@pytest.fixture
def sample_urdf_path(temp_dir):
"""Create a sample URDF file for testing."""
urdf_content = '''<?xml version="1.0"?>
<robot name="test_robot">
<link name="base_link">
<visual>
<geometry>
<box size="0.1 0.1 0.1"/>
</geometry>
</visual>
</link>
</robot>'''

urdf_path = temp_dir / "test_robot.urdf"
urdf_path.write_text(urdf_content)
return str(urdf_path)


@pytest.fixture
def mock_workspace_bounds():
"""Provide mock workspace bounds for testing."""
return {
"x_min": -0.5,
"x_max": 0.5,
"y_min": -0.5,
"y_max": 0.5,
"z_min": 0.0,
"z_max": 0.3
}
Empty file added tests/integration/__init__.py
Empty file.
83 changes: 83 additions & 0 deletions tests/test_setup_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Validation tests to ensure the testing infrastructure is properly set up."""

import pytest
from pathlib import Path


def test_pytest_working():
"""Basic test to verify pytest is working."""
assert True


def test_pytest_markers():
"""Test that custom markers are properly configured."""
# This test should pass and verify our markers work
pass


@pytest.mark.unit
def test_unit_marker():
"""Test that the unit marker works."""
assert 1 + 1 == 2


@pytest.mark.integration
def test_integration_marker():
"""Test that the integration marker works."""
assert "test" in "testing"


@pytest.mark.slow
def test_slow_marker():
"""Test that the slow marker works."""
import time
time.sleep(0.01) # Small sleep to simulate slow test
assert True


def test_fixtures_available(temp_dir, mock_config, mock_observation):
"""Test that fixtures from conftest.py are available."""
# Test temp_dir fixture
assert temp_dir.exists()
assert temp_dir.is_dir()

# Test mock_config fixture
assert isinstance(mock_config, dict)
assert "model" in mock_config
assert "training" in mock_config

# Test mock_observation fixture
assert isinstance(mock_observation, dict)
assert "state" in mock_observation


def test_beso_package_importable():
"""Test that the beso package can be imported."""
try:
import beso
assert hasattr(beso, '__init__')
except ImportError:
pytest.skip("BESO package dependencies not installed yet")


class TestProjectStructure:
"""Tests to validate the project structure."""

def test_project_root_exists(self):
"""Test that expected project files exist."""
project_root = Path(__file__).parent.parent

assert (project_root / "pyproject.toml").exists()
assert (project_root / "beso").exists()
assert (project_root / "beso" / "__init__.py").exists()

def test_test_structure_exists(self):
"""Test that test directory structure is correct."""
test_dir = Path(__file__).parent

assert (test_dir / "__init__.py").exists()
assert (test_dir / "conftest.py").exists()
assert (test_dir / "unit").exists()
assert (test_dir / "integration").exists()
assert (test_dir / "unit" / "__init__.py").exists()
assert (test_dir / "integration" / "__init__.py").exists()
Empty file added tests/unit/__init__.py
Empty file.