aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test_scenarios.py42
1 files changed, 36 insertions, 6 deletions
diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py
index e88547b..c1d315f 100644
--- a/tests/test_scenarios.py
+++ b/tests/test_scenarios.py
@@ -26,8 +26,8 @@ import traceback
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from idatui.app import ( # noqa: E402
ConfirmScreen, DecompView, DisasmView, FunctionsPanel, HexView, IdaTui,
- HelpScreen, ListingView, StringsPalette, StructEditor, SymbolPalette,
- XrefsScreen, _str_display, _word_occurrences,
+ HelpScreen, ListingView, QuitScreen, StringsPalette, StructEditor,
+ SymbolPalette, XrefsScreen, _str_display, _word_occurrences,
)
from textual.widgets import ( # noqa: E402
DataTable, Input, OptionList, Static, TextArea,
@@ -173,10 +173,17 @@ class Ctx:
await self.wait(lambda: self.lst.total > 0
and self.lst._cursor_ea() == fn.addr, t)
if view == "decomp":
- self.lst.focus()
- await self.press("tab") # F5/Tab -> decompile the function at cursor
- await self.wait(lambda: self.app._active == "decomp"
- and self.dec.loaded_ea == fn.addr, t)
+ # F5/Tab only decompiles from a focused code pane, and the listing
+ # may still be settling from the open above — a swallowed Tab used to
+ # surface much later as "pseudocode view shows: active=listing".
+ # Retry rather than assume the first one takes.
+ for _ in range(3):
+ self.lst.focus()
+ await self.pause(0.05)
+ await self.press("tab")
+ if await self.wait(lambda: self.app._active == "decomp"
+ and self.dec.loaded_ea == fn.addr, max(t / 3, 5)):
+ break
return fn
async def open_biggest(self, view="listing"):
@@ -341,6 +348,29 @@ async def s_command_palette(c: Ctx):
await c.wait(lambda: app._active != "hex", 5)
+@scenario("quit_guard")
+async def s_quit_guard(c: Ctx):
+ app = c.app
+ c.check("a clean database reports nothing unsaved", app._dirty_labels() == [],
+ f"{app._dirty_labels()}")
+ app._dirty = True # as an edit would
+ c.check("an edited database is reported unsaved",
+ len(app._dirty_labels()) == 1, f"{app._dirty_labels()}")
+ await c.press("q")
+ asked = await c.wait(lambda: isinstance(app.screen, QuitScreen), 10)
+ c.check("quitting with unsaved changes asks first", asked and app.is_running,
+ f"screen={type(app.screen).__name__} running={app.is_running}")
+ if not asked:
+ return
+ await c.press("escape")
+ await c.wait(lambda: not isinstance(app.screen, QuitScreen), 10)
+ c.check("Esc cancels the quit and stays put",
+ app.is_running and not isinstance(app.screen, QuitScreen))
+ # leave it clean so the rest of the suite (and teardown) isn't affected
+ app._dirty = False
+ app._save_on_exit = False
+
+
@scenario("help")
async def s_help(c: Ctx):
app = c.app