diff --git a/dzgui/config/freedesktop.py b/dzgui/config/freedesktop.py index 4cebe98..6e4504e 100644 --- a/dzgui/config/freedesktop.py +++ b/dzgui/config/freedesktop.py @@ -21,7 +21,7 @@ def write_desktop_file(exe_path: Path) -> Path: Terminal=false Exec={exe_path} Name=DZGUI - Comment=dzgui + Comment=DayZ server browser and mod manager Icon={icon} Categories=Game""" diff --git a/dzgui/util/redact.py b/dzgui/util/redact.py index 2cc57af..5d7254c 100644 --- a/dzgui/util/redact.py +++ b/dzgui/util/redact.py @@ -4,8 +4,9 @@ from typing import Literal api_filter = r"(.*&key=)([^&]*)(.*)" home_filter = r"(/home/)([^\s'\/]*)(.*)" +user_filter = r"(.*Steam/userdata/)([^/]*)(.*)" REDACTED = r"\1REDACTED\3" -REDACTION_PATTERNS = [api_filter, home_filter] +REDACTION_PATTERNS = [api_filter, home_filter, user_filter] def redact_home(text: str) -> str: diff --git a/dzgui/views/components/entry.py b/dzgui/views/components/entry.py index 88fd7e2..ee652a0 100644 --- a/dzgui/views/components/entry.py +++ b/dzgui/views/components/entry.py @@ -162,7 +162,8 @@ class APIEntry(Gtk.Box): self.entry.connect("icon-release", self._on_icon_release) self.entry.connect("activate", self._on_field_activated) - self.submit = Gtk.Button(label="Submit") + # TODO: strings + self.submit = Gtk.Button(label="Validate") self.submit.set_sensitive(False) self.submit.connect("clicked", self._on_submit) diff --git a/dzgui/views/dialogs/wizard.py b/dzgui/views/dialogs/wizard.py index 1efcf00..a012f9f 100644 --- a/dzgui/views/dialogs/wizard.py +++ b/dzgui/views/dialogs/wizard.py @@ -50,6 +50,13 @@ class PageNum(Enum): FINAL = 8 +class OptionalPageMixin: + """Marks optional pages as advanceable""" + + def _on_map(self, page: "ScrolledWizardPage") -> None: + EMITTER.emit("step_complete") + + class DescriptionArea(Gtk.Box): def __init__(self, text: str): super().__init__(orientation=Gtk.Orientation.VERTICAL) @@ -91,6 +98,7 @@ class ScrolledWizardPage(Gtk.ScrolledWindow): margin_top=50, spacing=20, ) + self.add(self.box) self.prog = Progress() self.box.pack_end(self.prog, expand=False, fill=False, padding=0) @@ -215,7 +223,7 @@ class APIValidationPage(ScrolledWizardPage): self.spinner.stop() -class BMValidationPage(APIValidationPage): +class BMValidationPage(OptionalPageMixin, APIValidationPage): # type: ignore def __init__(self) -> None: super().__init__( enum=PageNum.BM_API, @@ -224,6 +232,7 @@ class BMValidationPage(APIValidationPage): link=BM_API_SETUP, func=self._validate, ) + self.connect("map", self._on_map) @call_on_thread("", show_dialog=False) def _validate(self, key: str) -> None: @@ -445,6 +454,7 @@ class Assistant(Gtk.Assistant): else: self.set_default_size(1500, 900) + self.is_binary = False if os.getenv("PYAPP") is None else True self.config_path = XDG.config self.config_values: dict[str, Any] = config_boilerplate @@ -485,7 +495,7 @@ class Assistant(Gtk.Assistant): ): continue # NOTE: disabled for now on system-provided packages - if isinstance(page, ShortcutCreationPage) and os.getenv("PYAPP") is None: + if isinstance(page, ShortcutCreationPage) and not self.is_binary: continue self._add_page(page, page.get_page_type()) @@ -500,32 +510,33 @@ class Assistant(Gtk.Assistant): def _advance_page(self, index: int) -> int: page = self.get_nth_page(index) - # TODO: use enums/isinstance match page: - case self.page1: + case IntroductionPage(): pass - case self.page2: - if self.page2.is_migrated(): + case ConfigMigrationPage(): + if page.is_migrated(): steam_path = lookup(self.config_path, Preferences.DEFAULT) self.page7.set_steam_path(steam_path) - return self.get_n_pages() - 2 - case self.page3: + offset = 1 if not self.is_binary else 2 + self.setup_complete = True + return self.get_n_pages() - offset + case SteamPathPage(): self.config_values["default_steam_path"] = page.get_path_from_radio() - case self.page4: + case SteamValidationPage(): self.config_values["steam_api"] = page.get_api_key() - case self.page5: + case BMValidationPage(): self.config_values["bm_api"] = page.get_api_key() - # NOTE: collects config values before advancing to last page - case self.page6: + case PreferencesPage(): + # NOTE: collects config values before advancing to last page name, use_miles, client = self.page6.get_prefs() self.config_values["name"] = name self.config_values["use_miles"] = use_miles self.config_values["client"] = client self.write_config() self.page7.set_steam_path(self.config_values["default_steam_path"]) - case self.page7: - self.page7.create_shortcuts() self.setup_complete = True + case ShortcutCreationPage(): + page.create_shortcuts() case _: raise AttributeError("Trying to advance a non-canonical page") return index + 1 @@ -569,11 +580,7 @@ class Assistant(Gtk.Assistant): bar.set_fraction(fraction) bar.set_text(f"{page_num}/{total}") - # NOTE: disable forward action - # TODO: use page enums - if page == self.page5: - return - if page != self.page1: + if not isinstance(page, IntroductionPage): EMITTER.emit("step_pending") @@ -600,7 +607,7 @@ class CheckboxWithLabel(Gtk.Box): self.button.set_active(state) -class ShortcutCreationPage(ScrolledWizardPage): +class ShortcutCreationPage(OptionalPageMixin, ScrolledWizardPage): # type: ignore def __init__(self, shortcut: Path) -> None: super().__init__( enum=PageNum.SHORTCUTS, @@ -643,9 +650,6 @@ class ShortcutCreationPage(ScrolledWizardPage): self.desktop_checkbox.set_active(state) self.desktop_checkbox.set_sensitive(state) - def _on_map(self, page: "ScrolledWizardPage") -> None: - EMITTER.emit("step_complete") - def set_steam_path(self, path: Path) -> None: self.steam_path = path diff --git a/pyproject.toml b/pyproject.toml index 23eb8c6..8009c71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,11 +107,12 @@ config-settings-package = { pygobject-stubs = { config = "Gtk3,Gdk3,Soup2" } } [tool.pytest.ini_options] markers = [ + "apitest: checks remote endpoints", "config: config file keys/values", "mods: tests mod metadata/link creation", "pefile: validate PE files", "post_install: requires a completed installation", "redact: log redaction mechanisms", "slow: long-running tests", - "webtest: checks remote endpoints" + "webtest: depends on remote endpoint", ] diff --git a/tests/test_log_redaction.py b/tests/test_log_redaction.py index a96058c..438a195 100644 --- a/tests/test_log_redaction.py +++ b/tests/test_log_redaction.py @@ -37,6 +37,14 @@ class RecordsListHandler(logging.Handler): "Error in directory: '/home/SENSITIVE_USERNAME'", "Error in directory: '/home/REDACTED'", ), + ( + "User directory: /home/user/.local/share/Steam/userdata/999999/grid", + "User directory: /home/REDACTED/.local/share/Steam/userdata/REDACTED/grid", + ), + ( + "User directory: /drive/.local/share/Steam/userdata/999999", + "User directory: /drive/.local/share/Steam/userdata/REDACTED", + ), ], ) def test_log_redaction(log_error: str, expect: str) -> None: diff --git a/tests/test_shortcuts.py b/tests/test_shortcuts.py index 470b5c8..eea75e4 100644 --- a/tests/test_shortcuts.py +++ b/tests/test_shortcuts.py @@ -38,14 +38,14 @@ def dummy_app() -> None: return d -def test_wrap_exe(dummy_app) -> None: +def test_wrap_exe(dummy_app: dict[str, str]) -> None: s = Shortcuts(Path("")) s.add_shortcut(*dummy_app.values()) assert s.shortcuts["shortcuts"]["0"]["Exe"][0] == '"' assert s.shortcuts["shortcuts"]["0"]["Exe"][-1] == '"' -def test_add_shortcut(dummy_app) -> None: +def test_add_shortcut(dummy_app: dict[str, str]) -> None: s = Shortcuts(Path("")) s.add_shortcut(*dummy_app.values()) new = s.shortcuts["shortcuts"] @@ -56,7 +56,7 @@ def test_add_shortcut(dummy_app) -> None: assert new[ind][k] == v -def test_save_shortcut(dummy_app) -> None: +def test_save_shortcut(dummy_app: dict[str, str]) -> None: s = Shortcuts(Path("")) s.add_shortcut(*dummy_app.values()) with tempfile.NamedTemporaryFile() as f: @@ -65,3 +65,27 @@ def test_save_shortcut(dummy_app) -> None: s.save_shortcuts() s._load_shortcuts(tmp) assert len(s.shortcuts["shortcuts"]) == 1 + + +def test_shortcut_crc(dummy_app: dict[str, str]) -> None: + s = Shortcuts(Path("")) + s.add_shortcut(*dummy_app.values()) + for key in s.shortcuts["shortcuts"].keys(): + entry = s.shortcuts["shortcuts"][key] + name = entry["AppName"] + exe = entry["Exe"] + uid = name + exe + bpid = s.gen_bpid(uid) + assert entry["appid"] & 0xFFFFFFFF == bpid + + +def test_reverse_crc(dummy_app: dict[str, str]) -> None: + s = Shortcuts(Path("")) + s.add_shortcut(*dummy_app.values()) + for key in s.shortcuts["shortcuts"].keys(): + entry = s.shortcuts["shortcuts"][key] + name = entry["AppName"] + exe = entry["Exe"] + uid = name + exe + bpid = s.gen_bpid(uid) + assert s.find_appname_by_unsigned_id(bpid) == name