feat(node): push USB changes and preserve sensor setup across sessions
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import asyncio
|
||||
import threading
|
||||
|
||||
from k1link.fleet.events import FleetEvents
|
||||
|
||||
|
||||
def test_registry_events_cross_thread_coalesce_and_unsubscribe():
|
||||
async def check():
|
||||
events = FleetEvents()
|
||||
queue, close = events.subscribe()
|
||||
thread = threading.Thread(target=lambda: [events.notify() for _ in range(20)])
|
||||
thread.start()
|
||||
thread.join()
|
||||
await asyncio.sleep(0)
|
||||
assert queue.qsize() == 1
|
||||
await queue.get()
|
||||
close()
|
||||
events.notify()
|
||||
await asyncio.sleep(0)
|
||||
assert queue.empty()
|
||||
assert not events.listeners
|
||||
|
||||
asyncio.run(check())
|
||||
@@ -0,0 +1,83 @@
|
||||
"""Record finalization must leave the board inventory responsive."""
|
||||
|
||||
import importlib.util
|
||||
import threading
|
||||
import types
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_hashing_keeps_inventory_lock_free_and_rejects_new_capture(monkeypatch, tmp_path):
|
||||
monkeypatch.setitem(__import__("sys").modules, "pyrealsense2", types.ModuleType("pyrealsense2"))
|
||||
path = Path(__file__).parents[2] / "apps/node-agent/sensors/device.py"
|
||||
spec = importlib.util.spec_from_file_location("recording_device", path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
device = module.Device("synthetic-camera", tmp_path, {})
|
||||
ident = "capture_" + "a" * 32
|
||||
root = device.root / "recordings" / ident
|
||||
root.mkdir(parents=True)
|
||||
(root / "source.db3").write_bytes(b"synthetic record bytes")
|
||||
device.record = {"id": ident, "state": "recording"}
|
||||
entered, release = threading.Event(), threading.Event()
|
||||
real = module.hashlib.sha256
|
||||
|
||||
class SlowHash:
|
||||
def __init__(self):
|
||||
self.digest = real()
|
||||
|
||||
def update(self, value):
|
||||
entered.set()
|
||||
assert release.wait(3)
|
||||
self.digest.update(value)
|
||||
|
||||
def hexdigest(self):
|
||||
return self.digest.hexdigest()
|
||||
|
||||
monkeypatch.setattr(module.hashlib, "sha256", SlowHash)
|
||||
worker = threading.Thread(target=device.stop)
|
||||
worker.start()
|
||||
try:
|
||||
assert entered.wait(2)
|
||||
assert device.lock.acquire(timeout=0.2)
|
||||
try:
|
||||
assert device.acquisition == "stopping"
|
||||
with pytest.raises(ValueError):
|
||||
device.start()
|
||||
finally:
|
||||
device.lock.release()
|
||||
finally:
|
||||
release.set()
|
||||
worker.join(3)
|
||||
assert not worker.is_alive()
|
||||
assert device.acquisition == "idle"
|
||||
assert device.record is None
|
||||
assert module.json.loads((root / "manifest.json").read_text())["state"] == "complete"
|
||||
|
||||
|
||||
def test_disconnect_finalizes_failed_record_and_clears_live_frames(monkeypatch, tmp_path):
|
||||
monkeypatch.setitem(__import__("sys").modules, "pyrealsense2", types.ModuleType("pyrealsense2"))
|
||||
path = Path(__file__).parents[2] / "apps/node-agent/sensors/device.py"
|
||||
spec = importlib.util.spec_from_file_location("disconnect_device", path)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
device = module.Device("synthetic-camera", tmp_path, {})
|
||||
ident = "capture_" + "b" * 32
|
||||
root = device.root / "recordings" / ident
|
||||
root.mkdir(parents=True)
|
||||
(root / "source.db3").write_bytes(b"frames before physical disconnect")
|
||||
device.record = {"id": ident, "state": "recording"}
|
||||
device.images = {"color": b"old frame"}
|
||||
device.verified_this_process = True
|
||||
stopped = []
|
||||
device.pipeline = types.SimpleNamespace(stop=lambda: stopped.append(True))
|
||||
device.disconnect()
|
||||
assert stopped == [True]
|
||||
assert not device.online and not device.verified_this_process
|
||||
assert not device.images and device.pipeline is None
|
||||
assert device.acquisition == "failed"
|
||||
value = module.json.loads((root / "manifest.json").read_text())
|
||||
assert value["state"] == "failed" and value["sha256"]
|
||||
device.disconnect()
|
||||
assert stopped == [True]
|
||||
Reference in New Issue
Block a user