Skip to content
Merged
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
18 changes: 18 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Pytest configuration for Mujoco MCP tests."""

from __future__ import annotations

import asyncio
import inspect

import pytest


@pytest.hookimpl(tryfirst=True)
def pytest_pyfunc_call(pyfuncitem: pytest.Item) -> bool | None:
"""Allow ``async def`` tests to run without pytest-asyncio."""
test_func = getattr(pyfuncitem, "obj", None)
if inspect.iscoroutinefunction(test_func):
asyncio.run(test_func(**pyfuncitem.funcargs))
return True
Comment on lines +12 to +17
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix async hook to ignore non-parameter fixtures

CI is currently failing with TypeError: ... got an unexpected keyword argument 'event_loop_policy' coming from this hook. We call the coroutine with pyfuncitem.funcargs, which also includes fixtures injected via usefixtures/autouse (like event_loop_policy) that are not declared in the test signature; once we expand that dict we end up passing unexpected kwargs. Please filter the kwargs down to only the parameters the test actually declares before invoking the coroutine so the hook mirrors pytest’s normal call semantics.

     test_func = getattr(pyfuncitem, "obj", None)
     if inspect.iscoroutinefunction(test_func):
-        asyncio.run(test_func(**pyfuncitem.funcargs))
+        signature = inspect.signature(test_func)
+        accepts_kwargs = any(
+            param.kind == inspect.Parameter.VAR_KEYWORD
+            for param in signature.parameters.values()
+        )
+        call_kwargs = (
+            pyfuncitem.funcargs
+            if accepts_kwargs
+            else {
+                name: pyfuncitem.funcargs[name]
+                for name in signature.parameters
+                if name in pyfuncitem.funcargs
+            }
+        )
+        asyncio.run(test_func(**call_kwargs))
         return True
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def pytest_pyfunc_call(pyfuncitem: pytest.Item) -> bool | None:
"""Allow ``async def`` tests to run without pytest-asyncio."""
test_func = getattr(pyfuncitem, "obj", None)
if inspect.iscoroutinefunction(test_func):
asyncio.run(test_func(**pyfuncitem.funcargs))
return True
def pytest_pyfunc_call(pyfuncitem: pytest.Item) -> bool | None:
"""Allow ``async def`` tests to run without pytest-asyncio."""
test_func = getattr(pyfuncitem, "obj", None)
if inspect.iscoroutinefunction(test_func):
signature = inspect.signature(test_func)
accepts_kwargs = any(
param.kind == inspect.Parameter.VAR_KEYWORD
for param in signature.parameters.values()
)
call_kwargs = (
pyfuncitem.funcargs
if accepts_kwargs
else {
name: pyfuncitem.funcargs[name]
for name in signature.parameters
if name in pyfuncitem.funcargs
}
)
asyncio.run(test_func(**call_kwargs))
return True
🧰 Tools
🪛 GitHub Actions: Code Quality

[error] 16-16: TypeError in pytest runner: test_* function received an unexpected keyword argument 'event_loop_policy'. This originates from conftest.py and indicates a Pytest/pytest-asyncio compatibility issue. The CI step 'pytest --cov=src/mujoco_mcp --cov-report=xml --cov-report=html --cov-report=term-missing' failed with exit code 1.

🤖 Prompt for AI Agents
In conftest.py around lines 12 to 17, the async pytest hook calls the test
coroutine with pyfuncitem.funcargs which includes extra fixtures (like
autouse/usefixtures) not declared in the test signature and causes unexpected
keyword errors; before invoking asyncio.run, inspect the test function signature
to collect only parameter names actually declared by the test and filter
pyfuncitem.funcargs down to that set (e.g. build a kwargs dict containing only
keys present in the function's parameters), then call
asyncio.run(test_func(**filtered_kwargs)) and return True as before.

return None
13 changes: 13 additions & 0 deletions test_advanced_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Tests all new capabilities: controllers, coordination, sensors, RL, benchmarks, visualization
"""

import importlib.util
import time
import numpy as np
import sys
Expand All @@ -12,9 +13,21 @@
import tempfile
import json

import pytest

# Add project to path
sys.path.insert(0, str(Path(__file__).parent / "src"))

missing_optional = [
name for name in ("scipy", "gymnasium") if importlib.util.find_spec(name) is None
]

if missing_optional:
pytest.skip(
"Missing optional dependencies: " + ", ".join(missing_optional),
allow_module_level=True,
)

# Import all advanced modules
from mujoco_mcp.advanced_controllers import (
PIDController,
Expand Down
6 changes: 6 additions & 0 deletions test_debug.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
#!/usr/bin/env python3
"""Debug the action space issue"""

import importlib.util
import sys
from pathlib import Path
import numpy as np

import pytest

# Add src to path for imports
sys.path.insert(0, str(Path(__file__).parent / "src"))

if importlib.util.find_spec("gymnasium") is None:
pytest.skip("gymnasium is required for RL integration tests", allow_module_level=True)

from mujoco_mcp.rl_integration import create_balancing_env


Expand Down
20 changes: 11 additions & 9 deletions test_rl_advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,27 @@
Tests policy evaluation, training workflows, and advanced RL features
"""

import importlib.util
import sys
import time
import numpy as np
import json
from pathlib import Path
from typing import Dict, Any

import pytest

# Add src to path for imports
sys.path.insert(0, str(Path(__file__).parent / "src"))

try:
from mujoco_mcp.rl_integration import (
RLConfig, MuJoCoRLEnvironment, RLTrainer,
ReachingTaskReward, BalancingTaskReward, WalkingTaskReward,
create_reaching_env, create_balancing_env, create_walking_env
)
except ImportError as e:
print(f"❌ Import Error: {e}")
sys.exit(1)
if importlib.util.find_spec("gymnasium") is None:
pytest.skip("gymnasium is required for RL integration tests", allow_module_level=True)

from mujoco_mcp.rl_integration import (
RLConfig, MuJoCoRLEnvironment, RLTrainer,
ReachingTaskReward, BalancingTaskReward, WalkingTaskReward,
create_reaching_env, create_balancing_env, create_walking_env
)

class AdvancedRLTests:
"""Advanced RL functionality tests"""
Expand Down
27 changes: 14 additions & 13 deletions test_rl_functionality.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,30 @@
Tests all aspects of the MuJoCo MCP RL integration
"""

import importlib.util
import sys
import time
import numpy as np
import logging
from pathlib import Path
from typing import Dict, Any

import pytest

# Add src to path for imports
sys.path.insert(0, str(Path(__file__).parent / "src"))

try:
from mujoco_mcp.rl_integration import (
RLConfig, MuJoCoRLEnvironment, RLTrainer,
ReachingTaskReward, BalancingTaskReward, WalkingTaskReward,
create_reaching_env, create_balancing_env, create_walking_env,
example_training
)
from mujoco_mcp.viewer_client import MuJoCoViewerClient
from mujoco_mcp.simulation import MuJoCoSimulation
except ImportError as e:
print(f"❌ Import Error: {e}")
print("Make sure MuJoCo MCP is properly installed")
sys.exit(1)
if importlib.util.find_spec("gymnasium") is None:
pytest.skip("gymnasium is required for RL integration tests", allow_module_level=True)

from mujoco_mcp.rl_integration import (
RLConfig, MuJoCoRLEnvironment, RLTrainer,
ReachingTaskReward, BalancingTaskReward, WalkingTaskReward,
create_reaching_env, create_balancing_env, create_walking_env,
example_training
)
from mujoco_mcp.viewer_client import MuJoCoViewerClient
from mujoco_mcp.simulation import MuJoCoSimulation

# Configure logging
logging.basicConfig(level=logging.INFO)
Expand Down
22 changes: 12 additions & 10 deletions test_rl_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Tests RL functionality with actual MuJoCo physics simulation
"""

import importlib.util
import sys
import time
import subprocess
Expand All @@ -15,19 +16,20 @@
import threading
import logging

import pytest

# Add src to path for imports
sys.path.insert(0, str(Path(__file__).parent / "src"))

try:
from mujoco_mcp.rl_integration import (
RLConfig, MuJoCoRLEnvironment, RLTrainer,
create_reaching_env, create_balancing_env, create_walking_env
)
from mujoco_mcp.viewer_server import MuJoCoViewerServer
from mujoco_mcp.viewer_client import MuJoCoViewerClient
except ImportError as e:
print(f"❌ Import Error: {e}")
sys.exit(1)
if importlib.util.find_spec("gymnasium") is None:
pytest.skip("gymnasium is required for RL integration tests", allow_module_level=True)

from mujoco_mcp.rl_integration import (
RLConfig, MuJoCoRLEnvironment, RLTrainer,
create_reaching_env, create_balancing_env, create_walking_env
)
from mujoco_mcp.viewer_server import MuJoCoViewerServer
from mujoco_mcp.viewer_client import MuJoCoViewerClient

class RLIntegrationTest:
"""Test RL integration with MuJoCo viewer"""
Expand Down
20 changes: 11 additions & 9 deletions test_rl_simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,25 @@
Simplified RL Test - Tests core RL functionality without MuJoCo viewer dependency
"""

import importlib.util
import sys
import time
import numpy as np
from pathlib import Path

import pytest

# Add src to path for imports
sys.path.insert(0, str(Path(__file__).parent / "src"))

try:
from mujoco_mcp.rl_integration import (
RLConfig, MuJoCoRLEnvironment, RLTrainer,
create_reaching_env, create_balancing_env, create_walking_env,
example_training
)
except ImportError as e:
print(f"❌ Import Error: {e}")
sys.exit(1)
if importlib.util.find_spec("gymnasium") is None:
pytest.skip("gymnasium is required for RL integration tests", allow_module_level=True)

from mujoco_mcp.rl_integration import (
RLConfig, MuJoCoRLEnvironment, RLTrainer,
create_reaching_env, create_balancing_env, create_walking_env,
example_training
)

def test_rl_core_functionality():
"""Test core RL functionality without MuJoCo viewer"""
Expand Down
Loading