diff options
Diffstat (limited to 'tests/test_trace_ui.py')
| -rw-r--r-- | tests/test_trace_ui.py | 46 |
1 files changed, 29 insertions, 17 deletions
diff --git a/tests/test_trace_ui.py b/tests/test_trace_ui.py index 5844c90..855ce4d 100644 --- a/tests/test_trace_ui.py +++ b/tests/test_trace_ui.py @@ -19,7 +19,9 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from textual.widgets import Input, OptionList, Static # noqa: E402 -from _fixtures import staged # noqa: E402 +from _fixtures import fast_keys, staged # noqa: E402 + +fast_keys() # ~85ms -> ~2ms per keypress; see _fixtures.fast_keys from idatui._sync import settle # noqa: E402 from idatui.app import (DecompView, IdaTui, ListingView, # noqa: E402 RegWriteScreen, TraceDock) @@ -108,7 +110,7 @@ async def run() -> int: # -- stepping --------------------------------------------------- # lst = app.query_one(ListingView) lst.focus() - await pilot.pause(0.4) + await settle(app) await pilot.press("]") # `app._t` is assigned the moment the key is handled, so it is NOT a # signal that the VIEW has followed -- the navigation it kicks off @@ -126,7 +128,10 @@ async def run() -> int: timeout=20) check("[ steps backward", app._t == 0, f"t={app._t}") await pilot.press("[") - await pilot.pause(0.4) + # Nothing should happen, so there is no signal to wait FOR: the + # honest gate is "the app finished reacting" (workers drained), not + # a sleep long enough that a bug would have shown by now. + await settle(app) check("and stops at the start of the trace", app._t == 0) # -- step over -------------------------------------------------- # @@ -252,12 +257,14 @@ async def run() -> int: # opens the LISTING unless the decompiler is preferred — so # stepping through C used to drop you into disassembly on the # first keypress. Found by watching a demo, not by a test. + was = app._t await pilot.press("]") - await pilot.pause(1.2) + await settle(app, lambda: app._t != was) check("stepping in pseudocode stays in pseudocode", app._active == "decomp", f"active={app._active}") + was = app._t await pilot.press("[") - await pilot.pause(1.2) + await settle(app, lambda: app._t != was) check("and so does stepping backward", app._active == "decomp", f"active={app._active}") @@ -336,7 +343,10 @@ async def run() -> int: app._seek(s0) await wait(lambda: lst._cursor_ea() == dbaddr, pilot, 60) app._seek(s1) - await pilot.pause(3.0) # long enough for a stale one to land + # The stale navigation this guards against is a WORKER, so wait + # for the workers to drain rather than for three seconds and a + # hope: same question, ~50ms instead of 3s. + await settle(app) check("a stale navigation doesn't drag the cursor away", lst._cursor_ea() == dbaddr and app._cur.ea == dbaddr, f"cursor={lst._cursor_ea():#x} cur={app._cur.ea:#x} " @@ -354,47 +364,49 @@ async def run() -> int: else: if app._split: app.action_toggle_split() - await pilot.pause(1.0) + await settle(app, lambda: not app._split) if app._active != "listing": # focus() does NOT make a view active outside split mode; # Tab is what switches which one is showing. await pilot.press("tab") await wait(lambda: app._active == "listing", pilot, 60) app._seek(stamps[0]) - await pilot.pause(1.2) + await settle(app, lambda: app._t == stamps[0]) lst.focus() row = lst.model.index_of_ea(db) lst.cursor = row lst._scroll_cursor_into_view() - await pilot.pause(0.4) + await settle(app, lambda: lst._cursor_ea() == db) check("cursor is on the repeated instruction", lst._cursor_ea() == db, f"{lst._cursor_ea():#x} vs {db:#x}") await pilot.press(">") - await pilot.pause(1.0) + await settle(app, lambda: app._t == stamps[1]) check("> seeks to the next execution of it", app._t == stamps[1], f"t={app._t}, expected {stamps[1]}") status = str(app.query_one("#status", Static).render()) check("and says which execution this is", f"2 of {len(stamps)}" in status, status[:80]) lst.cursor = row - await pilot.pause(0.2) + await settle(app) await pilot.press("<") - await pilot.pause(1.0) + await settle(app, lambda: app._t == stamps[0]) check("< seeks back to the previous one", app._t == stamps[0], f"t={app._t}, expected {stamps[0]}") # An edge must SAY it's an edge rather than silently doing # nothing, which is indistinguishable from a broken key. lst.cursor = row - await pilot.pause(0.2) + await settle(app) await pilot.press("<") - await pilot.pause(1.0) + await settle(app, lambda: "first" in str( + app.query_one("#status", Static).render())) status = str(app.query_one("#status", Static).render()) check("and the first execution says so instead of moving", app._t == stamps[0] and "first" in status, status[:80]) # -- "which instruction set this register?" ---------------------- # - app._seek(min(200, t.length - 1)) - await pilot.pause(1.0) + want_t = min(200, t.length - 1) + app._seek(want_t) + await settle(app, lambda: app._t == want_t) lst.focus() await pilot.press("W") opened = await wait(lambda: isinstance(app.screen, RegWriteScreen), @@ -411,7 +423,7 @@ async def run() -> int: else: name, _v, last, _n = sc._rows[pick] sc.query_one(OptionList).highlighted = pick - await pilot.pause(0.3) + await settle(app) await pilot.press("enter") await wait(lambda: app._t == last, pilot, 30) check("choosing one seeks to the write that set it", |
