Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion .github/workflows/libero-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Replay a LIBERO demo through the env server (software rendering)
env:
MUJOCO_GL: osmesa
run: uv run pytest positronic/simulator/libero/tests/test_e2e.py --no-cov -rs
run: uv run pytest positronic/simulator/libero/tests/test_e2e.py -rs

# Reuses the LIBERO checkout the replay above cloned (``~/.cache/positronic/libero/src``). Validates the
# command transform (FK/IK + each controller's set_goal inverse) and the gripper normalization against
Expand Down
19 changes: 4 additions & 15 deletions .github/workflows/unit-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,10 @@ jobs:
- name: Install dependencies
run: uv sync --locked

- name: "Run tests (coverage: term, html)"
- name: Run tests
env:
PYTHONPATH: ${{ env.PYTHONPATH }}:$PWD
run: uv run pytest --cov-report=html

- name: Coverage summary (job summary)
if: always()
run: uv run coverage report -m >> $GITHUB_STEP_SUMMARY

- name: Upload coverage HTML artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-html-core-${{ matrix.os }}-${{ matrix.python }}
path: htmlcov
run: uv run pytest

packaging:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -81,7 +70,7 @@ jobs:
- name: Run lerobot vendor tests
env:
PYTHONPATH: ${{ env.PYTHONPATH }}:$PWD
run: uv run pytest positronic/vendors/lerobot_0_3_3/tests --no-cov
run: uv run pytest positronic/vendors/lerobot_0_3_3/tests

lerobot-latest:
runs-on: ubuntu-latest
Expand All @@ -101,7 +90,7 @@ jobs:
- name: Run lerobot vendor tests
env:
PYTHONPATH: ${{ env.PYTHONPATH }}:$PWD
run: uv run pytest positronic/vendors/lerobot/tests --no-cov
run: uv run pytest positronic/vendors/lerobot/tests

lockfile-portability:
strategy:
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

# Commands
- Every Python execution goes through `uv run --locked` — bare `python`/`pytest` bypasses the locked venv
- Run tests: `uv run --locked pytest --no-cov`
- Run single test file: `uv run --locked pytest path/to/test_file.py --no-cov`
- Run tests: `uv run --locked pytest`
- Run single test file: `uv run --locked pytest path/to/test_file.py`
- Lint: `uv run --locked ruff check --fix .`
- Format: `uv run --locked ruff format .`
- Run any Python: `uv run --locked python script.py`
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ repoints it at the tool.
Run tests and linters from the root directory:

```bash
uv run --locked pytest --no-cov
uv run --locked pytest
uv run --locked ruff check .
uv run --locked ruff format .
```
Expand Down
2 changes: 1 addition & 1 deletion docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ uv run pytest
With coverage report:

```bash
uv run pytest --cov=positronic --cov=pimm --cov-report=term-missing
uv run pytest --cov --cov-report=term-missing
```

## Pull Request Guidelines
Expand Down
29 changes: 24 additions & 5 deletions pimm/tests/test_world.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,20 @@ def test_world_defaults_to_system_clock(self):
assert isinstance(world.clock, SystemClock)


# Module scope: `start_in_subprocess` pickles the loop to reach a spawned child, and a class defined
# inside the test would not pickle.
class HeartbeatLoop:
Comment thread
vertix marked this conversation as resolved.
Outdated
"""Control loop announcing every iteration of its body."""

def __init__(self, emitter: SignalEmitter):
self.emitter = emitter

def run(self, stop_reader, clock):
while not stop_reader.read().data:
self.emitter.emit('beat')
yield Sleep(0.01)


class TestWorld:
"""Test the World class."""

Expand All @@ -244,17 +258,22 @@ def test_background_process(self):
"""Test that background processes will run simple control loop."""
world = World()
with world:
world.start_in_subprocess(dummy_process)
emitter, receiver = world.mp_pipes()
assert isinstance(receiver, SignalReceiver)
world.start_in_subprocess(HeartbeatLoop(emitter).run)

time.sleep(0.2) # Some time to let the process run
assert len(world.background_processes) == 1
Comment thread
vertix marked this conversation as resolved.

# Stopping before the child reaches its loop body would leave the loop untested: the generator
# sees a set event on its first condition and returns without ever running the body.
deadline = time.monotonic() + 30
while receiver.read() is None:
assert time.monotonic() < deadline, 'background process never entered its control loop'
time.sleep(0.01)

# We have to set the private event manually, because out of the scope of the context manager
# we can't access exit code of the process
world._stop_event.set()
# The child is spawned, so it boots a fresh interpreter and imports this module before it can
# observe the stop event — on a slow runner that outlasts any tight deadline. `join` returns the
# moment it exits, so a generous ceiling costs a passing run nothing.
world.background_processes[0].join(timeout=30)
assert not world.background_processes[0].is_alive()
assert world.background_processes[0].exitcode == 0
Expand Down
2 changes: 1 addition & 1 deletion positronic/simulator/libero/tests/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

then run by explicit path (macOS renders via GLFW, no env var; Linux needs ``MUJOCO_GL=osmesa``)::

uv run --locked pytest positronic/simulator/libero/tests/test_e2e.py --no-cov
uv run --locked pytest positronic/simulator/libero/tests/test_e2e.py
"""

import os
Expand Down
4 changes: 0 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,6 @@ addopts = [
# The suite waits on spawned processes and sockets about as much as it computes, so it scales with cores.
# `-n0` puts it back in one process, which live logs and `--pdb` need.
"-n", "auto",
"--cov=pimm",
"--cov=platform_client",
"--cov=positronic",
"--cov-report=term-missing",
# Needs the `libero` extra and OSMesa; `libero-e2e.yaml` runs it by path, which `--ignore` allows.
"--ignore=positronic/simulator/libero/tests/test_e2e.py",
]
Expand Down
Loading