diff --git a/tests/test_link.py b/tests/test_link.py index 01e2618..9ef85d8 100644 --- a/tests/test_link.py +++ b/tests/test_link.py @@ -118,6 +118,27 @@ class Connecting(unittest.TestCase): explain=explain) self.addCleanup(self.link.stop) + def test_start_routes_to_the_saved_headset_before_serving(self): + a = self.reg.add_device("frame", hosts=["192.0.2.1"]) + b = self.reg.add_device("frame-2", hosts=["192.0.2.2"]) + self.reg.set_active(b["id"]) + with mock.patch.object(self.link, "run", lambda: None): # no connector: only what start() applies + self.link.start() + self.assertEqual(self.routes[0][0], "frame-2") + self.assertIn("HostName=192.0.2.2", self.routes[0][1]) + self.assertNotEqual(a["id"], b["id"]) + + def test_headset_removed_elsewhere_mid_install_reaches_nothing(self): + a = self.reg.add_device("frame", hosts=["192.0.2.1"]) + self.reg.add_device("frame-2", hosts=["192.0.2.2"]) + self.reg.set_active(a["id"]) + other = fd.Registry(self.dir / "devices.json") # another Frame Control server + other.remove_device(a["id"]) + self.link.work = lambda: 1 + self.assertTrue(self.link.active_device().get("none")) + self.link.work = lambda: 0 + self.assertEqual(self.link.active_device()["alias"], "frame-2") # idle: move on + def listen6(self): """A "different device": the same port on IPv6 loopback.""" try: diff --git a/ui/frame_devices.py b/ui/frame_devices.py index 146bcb9..8345302 100644 --- a/ui/frame_devices.py +++ b/ui/frame_devices.py @@ -464,8 +464,10 @@ class Registry: # The headset in use is this server's own choice: another server picking a # different one mustn't move commands (an install, say) under it. The file's # choice is only where a server starts. + # Kept even if another server removed it, so the connector can see that + # (and not quietly move to another headset in the middle of an install). mine = self.data.get("active") - if mine and any(d["id"] == mine for d in data["devices"]): + if mine: data["active"] = mine self.data = data diff --git a/ui/frame_link.py b/ui/frame_link.py index 794938e..dba31a5 100644 --- a/ui/frame_link.py +++ b/ui/frame_link.py @@ -242,6 +242,11 @@ class Link: # ---- control from the server ---- def start(self): + # Route to the saved headset before the server takes requests: until the connector + # has run, commands (an upload by scp, say) would otherwise go to the default alias. + device = self.active_device() + with self.route_lock: + self.apply(device["alias"], self.first_route(device)) self.thread = threading.Thread(target=self.run, name="frame-link", daemon=True) self.thread.start() @@ -348,7 +353,10 @@ class Link: try: return self.reg.get(want) except frame_devices.DeviceError: - pass + # Removed (by another server). Mid-install, fail closed: the rest of the + # install, or its clean-up, mustn't land on whichever headset comes next. + if self.work(): + return self.NONE devices = self.reg.devices() if devices: return devices[0]