mirror of
https://github.com/MCKero6423/uv-k5-v3-emulator.git
synced 2026-10-02 03:15:36 +00:00
Log power events, QEMU stderr and firmware serial; show them in a fixed pane
Three sources into one buffer: power events from the supervisor, QEMU's stderr (which run.sh and the tests used to discard), and firmware serial, which the machine model tags SERIAL. default_launcher now captures stderr rather than sending it to DEVNULL, which is what made the last two reachable. The pane is a fixed-height scroll box as asked: 180px with overflow-y:auto, so it never grows with content -- older lines move up out of view and you scroll back to read them. Two details that make that usable rather than annoying: - Autoscroll only sticks when you are already at the bottom. Otherwise a new line arriving would yank the view away from whatever you had scrolled up to read. - MAX_LOG_LINES caps the <pre> as well. The box is fixed-height either way, but an unbounded DOM node would still grow memory across a long session. Verified on the live server: power on produced power/qemu/serial lines including "UV-K5 Firmware, EGZUMER-F4HWN+NR7Y c91cec95", each Reset logs the event and the banner reappearing, and since= never resent a line. A capacity-500 buffer fed 2000 lines keeps exactly 500 and does not replay evicted entries to a stale cursor.
This commit is contained in:
1 parent
7db89231b6
commit
c164917632
4 files changed
+187
-11
No files matched your search
@@ -506,5 +506,53 @@ class TestOptimisticSend(unittest.TestCase):
|
||||
self.assertLess(webui.TAP_MS, 400)
|
||||
|
||||
|
||||
class TestLogsEndpoint(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.client, self.http = make_app()
|
||||
|
||||
def test_logs_endpoint_returns_entries(self):
|
||||
resp = self.http.get("/api/logs")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
body = resp.get_json()
|
||||
self.assertIn("entries", body)
|
||||
self.assertIn("cursor", body)
|
||||
|
||||
def test_logs_accept_a_since_cursor(self):
|
||||
resp = self.http.get("/api/logs?since=0")
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
def test_logs_exposes_the_buffer(self):
|
||||
_, sup, http = make_supervised()
|
||||
app_log = http.application.config.get("LOG")
|
||||
self.assertIsNotNone(app_log)
|
||||
|
||||
|
||||
class TestLogPane(unittest.TestCase):
|
||||
def setUp(self):
|
||||
_, http = make_app()
|
||||
self.body = http.get("/").get_data(as_text=True)
|
||||
|
||||
def test_page_has_a_log_pane(self):
|
||||
self.assertIn('id="logpane"', self.body)
|
||||
self.assertIn("/api/logs", self.body)
|
||||
|
||||
def test_pane_has_a_fixed_height(self):
|
||||
"""The container must not grow with content."""
|
||||
self.assertIn("#logtext", self.body)
|
||||
self.assertIn("height:", self.body)
|
||||
|
||||
def test_pane_scrolls_rather_than_expanding(self):
|
||||
self.assertIn("overflow-y:auto", self.body)
|
||||
|
||||
def test_pane_is_capped_in_line_count(self):
|
||||
"""Even a scrollable pane needs a cap, or the DOM grows forever."""
|
||||
self.assertIn("MAX_LOG_LINES", self.body)
|
||||
|
||||
def test_autoscroll_yields_to_manual_scrolling(self):
|
||||
"""Scrolling up to read must not be yanked back by the next line."""
|
||||
self.assertIn("scrollHeight", self.body)
|
||||
self.assertIn("clientHeight", self.body)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user