Files
wehub-resource-sync 2114b14ee0
Sync main into demo / sync (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:26 +08:00

105 lines
3.3 KiB
Python

"""
Shared pytest fixtures for bench_env task testing.
Usage:
# Run offline tests only (no simulator needed):
pytest bench_env/tests/ -m "not live"
# Run all tests (simulator must be running at localhost:3000):
pytest bench_env/tests/
# Override simulator URL:
pytest bench_env/tests/ --sim-url http://localhost:3001
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
import pytest
import pytest_asyncio
from bench_env.env.mobile_gym import MobileGymEnv
from bench_env.env.base import Observation
from bench_env.task.judge import JudgeInput
# ── CLI options ─────────────────────────────────────────────────────
def pytest_addoption(parser):
parser.addoption(
"--sim-url",
default="http://localhost:3000",
help="Simulator URL (default: http://localhost:3000)",
)
# ── Markers ─────────────────────────────────────────────────────────
def pytest_configure(config):
config.addinivalue_line("markers", "live: requires running simulator")
# ── Environment fixture (shared browser for all live tests) ────────────
@pytest_asyncio.fixture(scope="session", loop_scope="session")
async def env(request) -> MobileGymEnv:
url = request.config.getoption("--sim-url")
e = MobileGymEnv(
url=url,
headless=True,
coord_space="norm_0_1000",
delay_after_action=0.3,
verbose=False,
viewport_size=(360, 800),
physical_size=(1080, 2400),
device_scale_factor=3,
)
await e.start()
yield e
await e.close()
# ── Defaults.json (for offline tests) ──────────────────────────────
@pytest.fixture(scope="session")
def railway_defaults() -> dict[str, Any]:
p = Path(__file__).resolve().parents[2] / "apps" / "Railway12306" / "data" / "defaults.json"
return json.loads(p.read_text(encoding="utf-8"))
# ── Helpers ─────────────────────────────────────────────────────────
def make_judge_input(
init_state: dict[str, Any],
curr_state: dict[str, Any],
*,
route: dict[str, Any] | None = None,
init_route: dict[str, Any] | None = None,
answer: str | None = None,
) -> JudgeInput:
"""Build a JudgeInput from raw state dicts.
States should already contain the full structure, e.g.:
{"apps": {"railway12306": {...}}, "os": {...}}
``route`` sets the *current* (last_obs) route.
``init_route`` sets the *initial* (init_obs) route; defaults to ``{}``
so that init and curr routes are independent.
"""
init_obs = Observation(
screenshot_base64="",
route=init_route or {},
state=init_state,
step_idx=0,
)
curr_obs = Observation(
screenshot_base64="",
route=route or {},
state=curr_state,
step_idx=1,
)
return JudgeInput(init_obs=init_obs, last_obs=curr_obs, answer=answer)