summaryrefslogtreecommitdiff
path: root/tools/ui_flow.py
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-20 01:51:05 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-20 01:51:05 +0200
commit42f9331e693f5058fcfcd7aef5a92cabc04c20ee (patch)
treef3dab2657eb592ee468e6cebc9b47d9da7e08683 /tools/ui_flow.py
parentd537f4e154a40075405b0ab2fed14fbe2eeaac81 (diff)
test(import): cover parsers, importer and a seeded fuzz corpus; import 100 entries in the UI flow
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tools/ui_flow.py')
-rw-r--r--tools/ui_flow.py34
1 files changed, 30 insertions, 4 deletions
diff --git a/tools/ui_flow.py b/tools/ui_flow.py
index f43cf81..3e82231 100644
--- a/tools/ui_flow.py
+++ b/tools/ui_flow.py
@@ -19,7 +19,8 @@ drawing each screen, so every step waits for the screen it expects instead
of sleeping. Menus are native Menu2 views that print nothing; for those the
script waits a fixed time.
-The flow covers: the list, a TOTP entry, a HOTP entry and its next code, the
+The flow covers: the results of an Aegis import sent through the settings
+form (the fixture sets the form, as the phone would), the list, a TOTP entry, a HOTP entry and its next code, the
PIN screen, a wrong PIN, a correct PIN opening a locked entry, auto-lock
closing it, the menu, the About screen (watches with buttons; touch-only
menus have no focus to move) and the lockout after 5 wrong PINs.
@@ -46,6 +47,8 @@ import sim_screenshot as shots
EXIT_OK, EXIT_DIFFERENT, EXIT_FLOW = 0, 1, 2
SCREEN_TIMEOUT_S = 20.0
AUTO_LOCK_TIMEOUT_S = 25.0 # the UI-test build locks after 10 s without input
+IMPORT_TIMEOUT_S = 90.0 # the 100-entry fixture import runs on a timer after start-up
+IMPORT_MEMORY_BUDGET = 0.70 # README 18: peak memory at most 70 % of the watch-app memory
MENU_SETTLE_S = 1.5 # Menu2 has no "shown" line
SETTLE_S = 0.4 # after a "shown" line, before the screenshot
CLICK_PAUSE_S = 0.25
@@ -225,6 +228,12 @@ class Flow:
else:
self.sim.swipe(self._w() // 2, self._h() * 3 // 4, self._w() // 2, self._h() // 4)
+ def prev(self) -> None:
+ if self.sim.has("up"):
+ self.sim.press("up")
+ else:
+ self.sim.swipe(self._w() // 2, self._h() // 4, self._w() // 2, self._h() * 3 // 4)
+
def back(self) -> None:
self.sim.press("esc")
@@ -305,9 +314,26 @@ class Flow:
self.results[name] = shots.check_golden(screen, label, out, golden)
+def flow_import(f: Flow) -> None:
+ """The fixture's 100-entry Aegis import through the settings form opens its
+ results; its peak memory must stay within the budget."""
+ f.expect("results", IMPORT_TIMEOUT_S)
+ stats = f.log.latest(r"DIAG import entries=(\d+) wall_ms=(\d+) mem_peak=(\d+) mem_total=(\d+)")
+ if stats is None:
+ raise FlowError("the import didn't report its statistics")
+ entries, wall_ms, peak, total = (int(v) for v in stats.groups())
+ fraction = peak / total
+ print(f"[{f.sim.device_dir.name}] import of {entries} entries: {wall_ms} ms, peak memory {fraction:.1%}")
+ if fraction > IMPORT_MEMORY_BUDGET:
+ raise FlowError(f"import peak memory {fraction:.1%} is over {IMPORT_MEMORY_BUDGET:.0%}")
+ f.shot("import_results")
+ f.act()
+ f.back()
+ f.expect("list")
+
+
def flow_entries(f: Flow) -> None:
"""The list, a TOTP entry, a HOTP entry and its next code."""
- f.expect("list")
f.shot("list")
f.act()
f.open_focused_row() # GitHub, TOTP
@@ -333,8 +359,7 @@ def flow_entries(f: Flow) -> None:
def flow_locked_entry(f: Flow) -> None:
"""Bank is locked: PIN screen, a wrong PIN, the right PIN, then auto-lock."""
- for _ in range(6): # wrap around to Bank (index 2 of 7)
- f.next()
+ f.prev() # from VPN back to Bank
f.act()
f.open_focused_row()
f.expect("pin")
@@ -391,6 +416,7 @@ def flow_lockout(f: Flow) -> None:
def run_flow(f: Flow) -> None:
"""The scripted session; see the module docstring."""
+ flow_import(f)
flow_entries(f)
flow_locked_entry(f)
flow_menu(f)