mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-09-16 14:52:59 +00:00
168 lines
5.9 KiB
Python
168 lines
5.9 KiB
Python
"""Popup wiring.
|
|
|
|
These are plain-Python checks of the parts that do not need a window, because
|
|
the bugs worth catching here are wiring mistakes, not pixels.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import inspect
|
|
from pathlib import Path
|
|
|
|
from epassport.ui import dialogs
|
|
|
|
|
|
def test_confirm_callback_is_not_named_with_an_on_prefix() -> None:
|
|
"""Kivy binds any ``on_*`` constructor keyword as an event observer.
|
|
|
|
Naming the callback property ``on_confirm`` meant ``ConfirmPopup(
|
|
on_confirm=fn)`` silently bound an observer and left the property None,
|
|
so confirming a deletion did nothing at all.
|
|
"""
|
|
names = [n for n in vars(dialogs.ConfirmPopup) if not n.startswith("__")]
|
|
assert "confirm_action" in names
|
|
assert not any(
|
|
n.startswith("on_")
|
|
and n not in ("on_touch_down", "on_touch_move", "on_touch_up")
|
|
for n in names
|
|
), "a callback property must not start with on_"
|
|
|
|
|
|
def test_confirm_runs_the_action() -> None:
|
|
calls: list[int] = []
|
|
popup = dialogs.ConfirmPopup.__new__(dialogs.ConfirmPopup)
|
|
popup.confirm_action = lambda: calls.append(1)
|
|
popup.dismiss = lambda *a, **k: None
|
|
dialogs.ConfirmPopup.confirm(popup)
|
|
assert calls == [1]
|
|
|
|
|
|
def test_confirm_without_an_action_is_harmless() -> None:
|
|
popup = dialogs.ConfirmPopup.__new__(dialogs.ConfirmPopup)
|
|
popup.confirm_action = None
|
|
popup.dismiss = lambda *a, **k: None
|
|
dialogs.ConfirmPopup.confirm(popup) # must not raise
|
|
|
|
|
|
def test_dump_rows_expose_both_actions() -> None:
|
|
assert "on_open" in dialogs.DumpRow.__events__
|
|
assert "on_delete" in dialogs.DumpRow.__events__
|
|
|
|
|
|
def test_deleting_asks_before_it_removes_anything() -> None:
|
|
source = inspect.getsource(dialogs.DumpsPopup.confirm_delete)
|
|
assert "ConfirmPopup" in source, "deletion must be confirmed, never immediate"
|
|
assert "cannot be undone" in source
|
|
|
|
|
|
# ------------------------------------------------------------- quick jumps
|
|
def test_shortcuts_offer_the_places_dumps_actually_live(monkeypatch, tmp_path) -> None:
|
|
from epassport import config
|
|
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
|
|
monkeypatch.setattr(config, "samples_dir", lambda: None)
|
|
labels = [label for label, _path in dialogs.ChooseDirPopup.shortcuts()]
|
|
assert "Dumps" in labels
|
|
assert "Home" in labels
|
|
assert "Samples" not in labels, "not offered until the samples are generated"
|
|
|
|
|
|
def test_samples_appear_once_generated(monkeypatch, tmp_path) -> None:
|
|
from epassport import config
|
|
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
|
|
generated = tmp_path / "samples"
|
|
generated.mkdir()
|
|
monkeypatch.setattr(config, "samples_dir", lambda: generated)
|
|
entries = dict(dialogs.ChooseDirPopup.shortcuts())
|
|
assert entries["Samples"] == generated.resolve()
|
|
|
|
|
|
def test_shortcuts_are_all_real_directories(monkeypatch, tmp_path) -> None:
|
|
from epassport import config
|
|
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
|
|
monkeypatch.setattr(config, "samples_dir", lambda: tmp_path / "does-not-exist")
|
|
for label, path in dialogs.ChooseDirPopup.shortcuts():
|
|
assert path.is_dir(), f"{label} points at {path}"
|
|
|
|
|
|
def test_duplicate_shortcuts_are_collapsed(monkeypatch, tmp_path) -> None:
|
|
"""Launched from the project root, 'Working dir' and 'Samples' can be the
|
|
same place; offering it twice is just noise."""
|
|
from epassport import config
|
|
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
|
|
monkeypatch.setattr(config, "samples_dir", lambda: Path.cwd())
|
|
paths = [path for _label, path in dialogs.ChooseDirPopup.shortcuts()]
|
|
assert len(paths) == len(set(paths))
|
|
|
|
|
|
def test_samples_dir_is_none_until_generated(tmp_path, monkeypatch) -> None:
|
|
from epassport import config
|
|
|
|
monkeypatch.setattr(config, "project_root", lambda: tmp_path)
|
|
assert config.samples_dir() is None
|
|
(tmp_path / "samples").mkdir()
|
|
assert config.samples_dir() == tmp_path / "samples"
|
|
|
|
|
|
# --------------------------------------------------- remembering where you were
|
|
def test_the_picker_reopens_beside_the_last_dump(tmp_path, monkeypatch) -> None:
|
|
"""A dump was picked *inside* some folder; offer that folder again.
|
|
|
|
Opening the app's own dumps directory every time meant navigating back to
|
|
wherever your dumps actually live on every single trip through the picker.
|
|
"""
|
|
from epassport import config
|
|
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
|
|
elsewhere = tmp_path / "captures" / "20260101T000000Z"
|
|
elsewhere.mkdir(parents=True)
|
|
assert config.browse_start(str(elsewhere)) == elsewhere.parent
|
|
|
|
|
|
def test_no_history_falls_back_to_the_app_dumps(tmp_path, monkeypatch) -> None:
|
|
from epassport import config
|
|
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
|
|
assert config.browse_start("") == config.dumps_dir()
|
|
|
|
|
|
def test_a_dump_that_has_been_deleted_falls_back(tmp_path, monkeypatch) -> None:
|
|
from epassport import config
|
|
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
|
|
gone = tmp_path / "gone" / "also-gone"
|
|
assert config.browse_start(str(gone)) == config.dumps_dir()
|
|
|
|
|
|
def test_a_deleted_dump_whose_parent_survives_offers_the_parent(
|
|
tmp_path, monkeypatch
|
|
) -> None:
|
|
from epassport import config
|
|
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
|
|
parent = tmp_path / "captures"
|
|
parent.mkdir()
|
|
assert config.browse_start(str(parent / "removed")) == parent
|
|
|
|
|
|
def test_the_start_is_absolute(tmp_path, monkeypatch) -> None:
|
|
"""The chooser is handed this path directly; a relative one is fragile."""
|
|
from epassport import config
|
|
|
|
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
|
|
nested = tmp_path / "rel" / "dump"
|
|
nested.mkdir(parents=True)
|
|
monkeypatch.chdir(tmp_path)
|
|
assert config.browse_start("rel/dump").is_absolute()
|
|
|
|
|
|
def test_recent_is_offered_as_a_shortcut() -> None:
|
|
import inspect
|
|
|
|
source = inspect.getsource(dialogs.ChooseDirPopup.shortcuts)
|
|
assert '"Recent"' in source
|
|
assert "browse_start" in source
|