diff --git a/qemu/tests/qmp.py b/qemu/tests/qmp.py index 627c0fb..4973a10 100755 --- a/qemu/tests/qmp.py +++ b/qemu/tests/qmp.py @@ -26,6 +26,12 @@ warden_debug.c. The same channel is what tools/warden-ctl reaches over SSH on a real panel, so these verbs mean the same thing on the rig and on hardware: nav MENU[/TAB] jump to a page; fails if the page is unknown + scroll X Y DY scroll the scrollable under pixel X,Y by DY + pixels (positive reveals content below), no + animation: the deterministic stand-in for a + swipe that only brings a control into view + home close every popup and go to Dashboard/Dashboard, + the state a fresh boot starts in wake wake the UI as if touched, minus the swallow (sleep.c dims and then blanks an idle panel and eats the touch that ends either); every compiled @@ -538,6 +544,16 @@ def drive(s, f, script_path, outdir, size, ctl_path=None, console_path=None, ref need_ctl(lineno, cmd) reply = ctl.send("wake") record(lineno, line, "ok" if reply == "wake: ok" else "fail", reply) + elif cmd in ("scroll", "home"): + # scroll X Y DY: the scrollable under the pixel moves DY pixels + # with no animation (warden_debug.c), the deterministic stand-in + # for a swipe whose only job was to bring a control into view. + # home: close every popup and return to Dashboard/Dashboard, the + # state a fresh boot starts in, so a live panel can run one flow + # after another. Both are judged by the channel's own ack. + need_ctl(lineno, cmd) + reply = ctl.send(line) + record(lineno, line, "ok" if reply.startswith(f"{cmd}: ok") else "fail", reply) elif cmd in ("page", "hit", "stats", "ctl"): # Query verbs: print the reply, never judge it. `ctl` is a raw # passthrough for anything the channel grows later. diff --git a/qemu/tests/test_qmp_drive.py b/qemu/tests/test_qmp_drive.py index 122c966..e3f59bf 100755 --- a/qemu/tests/test_qmp_drive.py +++ b/qemu/tests/test_qmp_drive.py @@ -42,6 +42,10 @@ class FakeCtl: return "page: Demo/Rows\ncpu: 12%\nfps: 10\nrender: 3.20 ms/frame\nrga: 0%\nidle: 0" if cmd == "wake": return "wake: ok" + if cmd == "home": + return "home: ok" + if cmd.startswith("scroll "): + return "scroll: ok y=300 of 900" if not cmd.endswith(" 0") else "scroll: expected X Y DY (DY non-zero pixels)" if cmd.startswith("@cat "): return json.dumps({"a": {"b": 1}, "list": [1, 2], "name": "warden"}) if cmd.startswith("hit "): @@ -129,9 +133,11 @@ class DriveVerbs(unittest.TestCase): "capture_region r1 0 0 8 8 exact\n" "assert_region r1 exact\n" "assert_region r1\n" + "scroll 360 400 300\n" + "home\n" ) self.assertIsNone(rc, [r for r in rows if r["status"] != "ok"]) - self.assertEqual(len(rows), 12) + self.assertEqual(len(rows), 14) self.assertTrue(all(r["status"] == "ok" for r in rows)) def test_mismatches_are_fails_not_stops(self): @@ -142,11 +148,12 @@ class DriveVerbs(unittest.TestCase): "wait_json a.b eq 2 1\n" "assert_json a.zz eq 1\n" "assert_stat fps lt 0\n" + "scroll 360 400 0\n" "assert_page Demo/Rows\n" ) self.assertEqual(rc, 1) self.assertEqual([r["status"] for r in rows], - ["fail", "fail", "fail", "fail", "fail", "ok"]) + ["fail", "fail", "fail", "fail", "fail", "fail", "ok"]) self.assertIn("moved", by["assert_hit 47 676 obj box=0,0,1x1"]["detail"]) self.assertGreaterEqual(time.monotonic() - t0, 1.0, "wait_json must honour its timeout")