From 86c8c4f543bbd5f24beb028ca4e51445fd456812 Mon Sep 17 00:00:00 2001 From: blasty Date: Thu, 6 Aug 2026 23:25:23 +0200 Subject: tests: wait for the thing, don't sleep and hope test_trace_ui spent 18.8 of its 35.2 seconds in flat pilot.pause() calls placed to let an async seek land. Two loops were most of it: 6 iterations at 0.5s and 28 at 0.3s, 11.4s of sleeping to check that a step moves the cursor. They are condition waits now. The questions are unchanged -- does the listing cursor reach the pc, does the pseudocode cursor follow -- but they cost what they cost instead of a fixed budget. The second loop settles on something that does NOT presuppose the answer (the listing cursor arriving, and the trail map belonging to the loaded function): waiting on 'is this pc mapped' would have burned the timeout on every unmapped instruction, about half of them, and come out slower than the sleep it replaced. 35.2s -> 20.9s, 39 checks, stable over repeated runs. tests/_fixtures.py collects the staging both this suite and test_scenarios need -- scratch copy, seeded from a golden .i64 nothing writes back to -- which was private to test_scenarios. Worth saying plainly: on targets/echo the seeding is worth 0.19s, not the analysis time I assumed when I went looking. It is shared for the deduplication and for whatever gets pointed at a bigger binary. --- tests/test_scenarios.py | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) (limited to 'tests/test_scenarios.py') diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 68ebfa3..0dbb0bd 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -24,12 +24,12 @@ import asyncio import fnmatch import os import re -import shutil import sys -import tempfile import traceback sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from _fixtures import staged # noqa: E402 from idatui.app import ( # noqa: E402 ConfirmScreen, DecompView, FunctionsPanel, GraphView, HexView, IdaTui, HelpScreen, ListingView, QuitScreen, StringsPalette, StructEditor, @@ -3137,25 +3137,6 @@ async def s_graph_sticky(c: Ctx): # --------------------------------------------------------------------------- # # Runner # --------------------------------------------------------------------------- # -async def _build_pristine(binary, cache): - """Analyse ``binary`` once and keep the resulting database as a golden copy. - - Costs one full analysis, then every later run starts from it instead of - re-analysing. - """ - print(f" (building pristine database for {os.path.basename(binary)}\u2026)") - app = IdaTui(open_path=binary, keepalive=False) - async with app.run_test(size=(140, 44)) as pilot: - for _ in range(6000): - await pilot.pause(0.05) - if app._func_index is not None and app._func_index.complete: - break - app.program.client.call("idb_save", timeout=600.0) - db = binary + ".i64" - if os.path.exists(db): - shutil.copy2(db, cache) - - async def run(binary, only=None): # The suite EDITS the database — it defines code, undefines items, renames # and comments — and IDA saves those edits. Run that against the tracked @@ -3167,15 +3148,8 @@ async def run(binary, only=None): # # So: work on a scratch copy, seeded from a golden database that nothing # ever writes back to. - with tempfile.TemporaryDirectory(prefix="idatui-pilot-") as scratch: - target = os.path.join(scratch, os.path.basename(binary)) - shutil.copy2(binary, target) - cache = binary + ".pristine.i64" - if not (os.path.exists(cache) - and os.path.getmtime(cache) >= os.path.getmtime(binary)): - await _build_pristine(target, cache) - if os.path.exists(cache): - shutil.copy2(cache, target + ".i64") + async with staged(binary, lambda p: IdaTui(open_path=p, keepalive=False), + prefix="idatui-pilot-") as target: await _run_on(target, only) -- cgit v1.3.1-sl0p