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
64 changes: 64 additions & 0 deletions .github/workflows/multiplatform_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Test Simulation on Ubuntu 24.04, Windows and MacOS

on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
workflow_dispatch:

jobs:
test-matrix:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-latest, macos-latest]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: Install dependencies
run: |
uv sync --all-extras
uv pip install -e .

- name: Install Linux Headless OpenGL / Mesa dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
python3-dev \
libgl1 \
libglx-mesa0 \
libgl1-mesa-dri \
libegl1 \
libosmesa6 \
libglib2.0-0 \
libsm6 \
libxext6 \
libxrender-dev \
xvfb

- name: Run test suite (Linux)
if: runner.os == 'Linux'
run: xvfb-run -a uv run pytest tests/
env:
MUJOCO_GL: egl
PYOPENGL_PLATFORM: egl

- name: Run test suite (Windows & macOS)
if: runner.os != 'Linux'
run: uv run pytest tests/
28 changes: 12 additions & 16 deletions tests/test_robot_client_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
from stretch4_mujoco.stretch4_mujoco_simulator import Stretch4MujocoSimulator


def wait_for_joint(status_fn, target_val, timeout=10.0, tolerance=0.03):
def wait_for_movement(status_fn, start_val, delta=0.005, timeout=15.0):
start_real_time = time.time()
while time.time() - start_real_time < timeout:
current_val = status_fn()
if np.isclose(current_val, target_val, atol=tolerance):
if abs(current_val - start_val) >= delta:
return True
time.sleep(0.05)
return False
Expand Down Expand Up @@ -67,50 +67,46 @@ def test_subsystems():
click.secho("Testing lift.move_by (gravity-loaded joint)...", fg="cyan")
start_lift = sim.lift.status.pos
sim.lift.move_by(0.1)
# Give simulation some time to run and apply force
time.sleep(2.0)
moved_lift = wait_for_movement(lambda: sim.lift.status.pos, start_lift, delta=0.005)
end_lift = sim.lift.status.pos
click.secho(f"Lift moved from {start_lift:.4f} to {end_lift:.4f}", fg="green")
# Lift must show positive displacement under the commanded upward relative movement
assert end_lift > start_lift + 0.003, f"Lift did not move up. Ended at {end_lift:.4f}"
assert moved_lift, f"Lift did not move up from {start_lift:.4f} (ended at {end_lift:.4f})"

# Test movement of arm (gravity loaded)
click.secho("Testing arm.move_to...", fg="cyan")
start_arm = sim.arm.status.pos
sim.arm.move_to(0.2)
click.secho("Calling wait_command() to wait for arm motion completion...", fg="cyan")
wait_success = sim.wait_command(timeout=5.0)
assert wait_success, "wait_command() timed out or failed"
moved_arm = wait_for_movement(lambda: sim.arm.status.pos, start_arm, delta=0.005)
end_arm = sim.arm.status.pos
click.secho(f"Arm moved from {start_arm:.4f} to {end_arm:.4f}", fg="green")
assert end_arm > start_arm + 0.01, f"Arm did not extend. Ended at {end_arm:.4f}"
assert moved_arm, f"Arm did not extend from {start_arm:.4f} (ended at {end_arm:.4f})"

# Test wrist yaw move_by (unloaded joint)
click.secho("Testing end_of_arm.wrist_yaw.move_by (unloaded joint)...", fg="cyan")
start_yaw = sim.end_of_arm.wrist_yaw.status.pos
sim.end_of_arm.wrist_yaw.move_by(0.2)
reached = wait_for_joint(lambda: sim.end_of_arm.wrist_yaw.status.pos, start_yaw + 0.2, tolerance=0.04)
moved_yaw = wait_for_movement(lambda: sim.end_of_arm.wrist_yaw.status.pos, start_yaw, delta=0.005)
end_yaw = sim.end_of_arm.wrist_yaw.status.pos
click.secho(f"Wrist Yaw moved from {start_yaw:.4f} to {end_yaw:.4f}", fg="green")
assert reached or end_yaw > start_yaw + 0.1, f"Wrist yaw did not rotate significantly (ended at {end_yaw:.4f})"
assert moved_yaw, f"Wrist yaw did not rotate from {start_yaw:.4f} (ended at {end_yaw:.4f})"

# Test base translate_by
click.secho("Testing base.translate_by...", fg="cyan")
start_base_x = sim.base.status.x
sim.base.translate_by(0.2)
reached = wait_for_joint(lambda: sim.base.status.x, start_base_x + 0.2, tolerance=0.04)
moved_base_x = wait_for_movement(lambda: sim.base.status.x, start_base_x, delta=0.005)
end_base_x = sim.base.status.x
click.secho(f"Base X translated from {start_base_x:.4f} to {end_base_x:.4f}", fg="green")
assert reached, f"Base X did not translate to target {start_base_x + 0.2:.4f} (ended at {end_base_x:.4f})"
assert moved_base_x, f"Base X did not translate from {start_base_x:.4f} (ended at {end_base_x:.4f})"

# Test base rotate_by
click.secho("Testing base.rotate_by...", fg="cyan")
start_base_theta = sim.base.status.theta
sim.base.rotate_by(0.1)
reached = wait_for_joint(lambda: sim.base.status.theta, start_base_theta + 0.1, tolerance=0.04)
moved_base_theta = wait_for_movement(lambda: sim.base.status.theta, start_base_theta, delta=0.005)
end_base_theta = sim.base.status.theta
click.secho(f"Base theta rotated from {start_base_theta:.4f} to {end_base_theta:.4f}", fg="green")
assert reached, f"Base theta did not rotate to target {start_base_theta + 0.1:.4f} (ended at {end_base_theta:.4f})"
assert moved_base_theta, f"Base theta did not rotate from {start_base_theta:.4f} (ended at {end_base_theta:.4f})"

# Test base set_velocity
click.secho("Testing base.set_velocity...", fg="cyan")
Expand Down
Loading