1"""One user's controller is touched by one request at a time: the streams and /reset too.
31298bf7a moved /init, /step, /simulate and /simulate-layer2 into ``run_compute`` (the thread pool,
4under the session's compute lock) and the SSE streams' controller steps onto bare pool threads.
5On the event loop the two kinds could only interleave between steps; on a bare thread a stream's
6step ran beside a /simulate or /reset of the same ``session.controller`` from another tab, mid-step.
9from __future__
import annotations
14pytest.importorskip(
"httpx", reason=
"fastapi TestClient needs httpx")
16from fastapi.testclient
import TestClient
19from backend
import session
as session_mod
22EMAIL =
"controller-lock@berkeley.edu"
23H = {
"X-Auth-Email": EMAIL}
31 return session_mod.registry.get(EMAIL).compute_lock.locked()
35 """Records whether the session's compute lock was held at each touch; never solves."""
39 def __init__(self, cfg, engine_config, logger=None):
42 FakeController.touches.append((
"build",
_lock_held()))
45 FakeController.touches.append((
"reset",
_lock_held()))
50 def step(self, meas, nav, cmd):
51 FakeController.touches.append((
"step",
_lock_held()))
52 raise _Stop(
"recorded")
57 FakeController.touches = []
58 monkeypatch.setattr(control_router,
"RobustDDPController", FakeController)
59 yield FakeController.touches
60 session_mod.registry.get(EMAIL).controller.controller =
None
64 client = TestClient(backend_main.app)
65 r = client.post(
"/api/control/simulate-layer2-stream", headers=H,
66 json={
"thrust_curve_time": [0.0, 0.1],
"thrust_curve_values": [6800.0, 6800.0]})
67 assert r.status_code == 200
68 assert "recorded" in r.text, r.text[:400]
69 kinds = [k
for k, _
in fake]
70 assert kinds[:3] == [
"build",
"reset",
"step"], fake
71 assert all(held
for _, held
in fake), (
72 f
"the stream touched the controller without the session's compute lock: {fake}. A /simulate "
73 "or /reset of the same controller could run beside it; go through run_compute."
78 client = TestClient(backend_main.app)
79 client.get(
"/api/health", headers=H)
80 session_mod.registry.get(EMAIL).controller.controller =
FakeController(
None,
None)
82 r = client.post(
"/api/control/reset", headers=H)
83 assert r.status_code == 200, r.text[:300]
84 assert fake == [(
"reset",
True)], f
"/reset touched the controller outside the compute lock: {fake}"
step(self, meas, nav, cmd)
__init__(self, cfg, engine_config, logger=None)
test_reset_waits_for_the_lock(fake)
test_a_streams_controller_is_built_and_stepped_under_the_lock(fake)