|
1 | | -"""Regression test (backend-free) for Libero90BaseTask._get_initial_states hardening. |
| 1 | +"""Regression tests (backend-free) for the libero task fixes. |
2 | 2 |
|
3 | | -The method must degrade to None (handler defaults) when the trajectory is |
4 | | -missing/empty or the scenario is robotless, matching the already-hardened |
5 | | -LiberoBaseTask. Previously it indexed ``scenario.robots[0]`` and called get_traj |
6 | | -with no guard, so those cases crashed. |
| 3 | +Covers two independent hardening fixes: |
| 4 | +
|
| 5 | +* ``Libero90BaseTask._get_initial_states`` must degrade to None (handler |
| 6 | + defaults) when the trajectory is missing/empty or the scenario is robotless, |
| 7 | + matching the already-hardened ``LiberoBaseTask``. Previously it indexed |
| 8 | + ``scenario.robots[0]`` and called get_traj with no guard, so those cases |
| 9 | + crashed. |
| 10 | +* Every ``libero_pick_*`` task must declare ``robots=["franka"]``. 9 of 10 |
| 11 | + omitted it, so they instantiated with an empty robot list — a robotless pick |
| 12 | + task can never succeed and eval scores a silent 0%. |
7 | 13 | """ |
8 | 14 |
|
9 | 15 | from __future__ import annotations |
10 | 16 |
|
| 17 | +import pathlib |
| 18 | + |
11 | 19 | import pytest |
12 | 20 |
|
| 21 | +_LIBERO = pathlib.Path(__file__).resolve().parents[1] / "roboverse_pack" / "tasks" / "libero" |
| 22 | + |
13 | 23 |
|
14 | 24 | @pytest.mark.general |
15 | 25 | def test_libero_90_get_initial_states_degrades_gracefully(monkeypatch): |
@@ -44,3 +54,28 @@ def _raise(*a, **k): |
44 | 54 | # 3. get_traj returns empty → None (empty guard before len()) |
45 | 55 | monkeypatch.setattr(mod, "get_traj", lambda *a, **k: ([], None, None)) |
46 | 56 | assert t._get_initial_states() is None |
| 57 | + |
| 58 | + |
| 59 | +@pytest.mark.general |
| 60 | +def test_all_libero_pick_tasks_have_a_robot(): |
| 61 | + """Every ``libero_pick_*`` task is a "pick up X and place in basket" task and |
| 62 | + therefore MUST declare a robot arm. Previously 9 of 10 omitted |
| 63 | + ``robots=["franka"]`` (only butter had it), so the scenario instantiated with |
| 64 | + an empty robot list — a robotless pick task can never succeed and eval scores |
| 65 | + a silent 0%. This pins every sibling to declare a robot. |
| 66 | + """ |
| 67 | + import importlib |
| 68 | + |
| 69 | + pick_files = sorted(_LIBERO.glob("libero_pick_*.py")) |
| 70 | + assert pick_files, "no libero_pick_* task files found" |
| 71 | + missing = [] |
| 72 | + for f in pick_files: |
| 73 | + mod = importlib.import_module(f"roboverse_pack.tasks.libero.{f.stem}") |
| 74 | + # the task class defines a class-level ScenarioCfg |
| 75 | + for obj in vars(mod).values(): |
| 76 | + scen = getattr(obj, "scenario", None) |
| 77 | + if scen is not None and hasattr(scen, "robots"): |
| 78 | + if not scen.robots: |
| 79 | + missing.append(f.stem) |
| 80 | + break |
| 81 | + assert not missing, f"libero_pick tasks with no robot (robotless pick can never succeed): {missing}" |
0 commit comments