FrameBridge 0.3.1 community source release

This commit is contained in:
FrameBridge Development committed 2026-10-26 22:09:18 -04:00
commit b23819fff1
1354 files changed
+343716

No files matched your search

+163
View File
@@ -0,0 +1,163 @@
import copy
from datetime import datetime, timezone, timedelta
import hashlib
import os
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
from framebridge_expanded.real_screen import screen_control as m
class ScreenControlTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.path = Path(self.temp.name) / "screen.png"
self.path.write_bytes(b"immutable-observed-frame-fixture")
self.binding = {"version": 1, "backend": "visible-tmux", "node": "TESTNODE",
"uid": os.getuid(), "active_vt": "tty1", "client_tty": "/dev/tty1",
"socket": {"path": "/run/user/1000/console.sock", "inode": 12},
"server": {"pid": 100, "start_ticks": 20},
"client": {"pid": 101, "start_ticks": 21},
"session_id": "$0", "window_id": "@0", "pane_id": "%1",
"pane": {"pid": 102, "start_ticks": 22},
"pane_geometry": [120, 0, 120, 66], "pane_in_mode": False}
self.manifest = {"capture_id": "a" * 32, "source": "linux-framebuffer",
"screen_type": "tty", "active_vt": "tty1", "node": "TESTNODE",
"uid": os.getuid(), "synthetic": False, "pixel_source_verified": True,
"path": str(self.path), "sha256": hashlib.sha256(self.path.read_bytes()).hexdigest(),
"completed_at": datetime.now(timezone.utc).isoformat(),
"binding": copy.deepcopy(self.binding)}
def tearDown(self):
self.temp.cleanup()
def refused(self, change, code="SCREEN_BINDING_CHANGED"):
different = copy.deepcopy(self.binding)
change(different)
with patch.object(m, "snapshot_binding", return_value=different), patch.object(m, "_tmux") as send:
with self.assertRaises(m.ControlError) as e:
m.act(self.manifest, "key.send", key="ENTER")
self.assertEqual(e.exception.code, code)
send.assert_not_called()
def test_bind_returns_copy(self):
with patch.object(m, "snapshot_binding", return_value=self.binding):
result = m.bind_screen(self.manifest)
self.assertIsNot(result, self.manifest)
self.assertEqual(result["binding"], self.binding)
def test_bind_vt_mismatch(self):
self.manifest["active_vt"] = "tty2"
with patch.object(m, "snapshot_binding", return_value=self.binding):
with self.assertRaises(m.ControlError) as e:
m.bind_screen(self.manifest)
self.assertEqual(e.exception.code, "SCREEN_CAPTURE_VT_MISMATCH")
def test_bind_node_mismatch(self):
self.manifest["node"] = "OTHER"
with patch.object(m, "snapshot_binding", return_value=self.binding):
with self.assertRaises(m.ControlError) as e:
m.bind_screen(self.manifest)
self.assertEqual(e.exception.code, "SCREEN_CAPTURE_NODE_MISMATCH")
def test_wrong_source(self):
self.manifest["source"] = "rendered-text"
with self.assertRaises(m.ControlError) as e:
m.bind_screen(self.manifest)
self.assertEqual(e.exception.code, "SCREEN_CONTROL_MODE_UNSUPPORTED")
def test_literal_text_no_shell_or_enter(self):
text = "printf '%s' '$(touch never)'"
with patch.object(m, "snapshot_binding", return_value=self.binding), patch.object(m, "_tmux", return_value="") as send:
result = m.act(self.manifest, "text.type", text=text)
self.assertTrue(result["performed"])
send.assert_called_once_with(self.binding["socket"]["path"], "send-keys", "-t", "%1", "-l", "--", text)
def test_explicit_key(self):
with patch.object(m, "snapshot_binding", return_value=self.binding), patch.object(m, "_tmux", return_value="") as send:
result = m.act(self.manifest, "key.send", key="ENTER")
self.assertTrue(result["performed"])
send.assert_called_once_with(self.binding["socket"]["path"], "send-keys", "-t", "%1", "Enter")
def test_text_newline_rejected(self):
with patch.object(m, "_tmux") as send:
with self.assertRaises(m.ControlError) as e:
m.act(self.manifest, "text.type", text="pwd\n")
self.assertEqual(e.exception.code, "SCREEN_TEXT_CONTROL_CHARACTER")
send.assert_not_called()
def test_unknown_key_rejected(self):
with patch.object(m, "_tmux") as send:
with self.assertRaises(m.ControlError):
m.act(self.manifest, "key.send", key="ALT_F2")
send.assert_not_called()
def test_changed_vt_no_input(self):
self.refused(lambda b: b.update(active_vt="tty2"))
def test_changed_pane_no_input(self):
self.refused(lambda b: b["pane"].update(pid=222))
def test_pid_reuse_no_input(self):
self.refused(lambda b: b["pane"].update(start_ticks=23))
def test_changed_socket_no_input(self):
self.refused(lambda b: b["socket"].update(inode=13))
def test_changed_geometry_no_input(self):
self.refused(lambda b: b.update(pane_geometry=[0, 0, 240, 66]))
def test_hidden_client_no_fallback(self):
identity = {"path": "/run/user/1000/console.sock", "inode": 12}
client_row = "101|/dev/tty2|$0|240|67|0\n"
with patch.object(m, "_active_vt", return_value="tty1"), patch.object(m, "_server_sockets", return_value=[(identity, {"pid": 100})]), patch.object(m, "_tmux", return_value=client_row) as run:
with self.assertRaises(m.ControlError) as e:
m.snapshot_binding()
self.assertEqual(e.exception.code, "SCREEN_VISIBLE_TMUX_NOT_FOUND")
self.assertEqual(run.call_count, 1)
def test_postflight_change_reports_performed(self):
other = copy.deepcopy(self.binding)
other["pane_id"] = "%2"
with patch.object(m, "snapshot_binding", side_effect=[self.binding, self.binding, other]), patch.object(m, "_tmux", return_value="") as send:
with self.assertRaises(m.ControlError) as e:
m.act(self.manifest, "key.send", key="ENTER")
self.assertEqual(e.exception.code, "SCREEN_CHANGED_AFTER_INPUT")
self.assertTrue(e.exception.details["performed"])
send.assert_called_once()
def test_tampered_capture_no_input(self):
self.path.write_bytes(b"changed")
with patch.object(m, "_tmux") as send:
with self.assertRaises(m.ControlError) as e:
m.act(self.manifest, "key.send", key="ENTER")
self.assertEqual(e.exception.code, "SCREEN_CAPTURE_SHA256_MISMATCH")
send.assert_not_called()
def test_capture_id_required(self):
self.manifest.pop("capture_id")
with self.assertRaises(m.ControlError) as e:
m.act(self.manifest, "key.send", key="ENTER")
self.assertEqual(e.exception.code, "SCREEN_CAPTURE_ID_REQUIRED")
def test_copy_mode_does_not_type_into_hidden_application(self):
self.binding["pane_in_mode"] = True
self.manifest["binding"] = copy.deepcopy(self.binding)
self.refused(lambda b: None, "SCREEN_TMUX_MODE_INPUT_UNSUPPORTED")
def test_optional_explicit_age_limit(self):
self.manifest["completed_at"] = (datetime.now(timezone.utc) - timedelta(hours=1)).isoformat()
with self.assertRaises(m.ControlError) as e:
m.act(self.manifest, "key.send", key="ENTER", max_age_s=30)
self.assertEqual(e.exception.code, "SCREEN_CAPTURE_EXPIRED")
def test_no_arbitrary_age_expiry_by_default(self):
self.manifest["completed_at"] = (datetime.now(timezone.utc) - timedelta(hours=1)).isoformat()
with patch.object(m, "snapshot_binding", return_value=self.binding), patch.object(m, "_tmux", return_value=""):
self.assertTrue(m.act(self.manifest, "key.send", key="ENTER")["performed"])
if __name__ == "__main__":
unittest.main()