diff options
| author | Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> | 2026-09-13 11:53:37 -0400 |
|---|---|---|
| committer | Kurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com> | 2026-09-13 11:53:37 -0400 |
| commit | d9c6a48dead8e521912fc64a109e72ec7811082d (patch) | |
| tree | e98145365c3c178d60434401529734e193bf1f0d | |
| parent | daadf150e7c89a7725045f497328540959433082 (diff) | |
| download | decky-lsfg-vk-d9c6a48dead8e521912fc64a109e72ec7811082d.tar.gz decky-lsfg-vk-d9c6a48dead8e521912fc64a109e72ec7811082d.zip | |
fix: simplify Flatpak reset ownership
| -rw-r--r-- | py_modules/lsfg_vk/flatpak_profile_service.py | 60 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/flatpak_service.py | 87 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/plugin.py | 2 | ||||
| -rw-r--r-- | tests/test_flatpak_profile_service.py | 91 | ||||
| -rw-r--r-- | tests/test_flatpak_service.py | 64 |
5 files changed, 96 insertions, 208 deletions
diff --git a/py_modules/lsfg_vk/flatpak_profile_service.py b/py_modules/lsfg_vk/flatpak_profile_service.py index 9d2d104..3919159 100644 --- a/py_modules/lsfg_vk/flatpak_profile_service.py +++ b/py_modules/lsfg_vk/flatpak_profile_service.py @@ -1,6 +1,5 @@ from __future__ import annotations -import re from typing import Any, Dict from .configuration import ConfigurationService @@ -17,10 +16,6 @@ class FlatpakProfileService: "enableZink", ) BOOLEAN_FIELDS = STATE_FIELDS[1:] - DXVK_FRAME_RATE_SEGMENT = re.compile( - r"^(?:dxvk\.maxFrameRate|dxgi\.maxFrameRate|d3d9\.maxFrameRate)\s*=", - re.IGNORECASE, - ) def __init__( self, @@ -64,14 +59,6 @@ class FlatpakProfileService: raise RuntimeError("Flatpak application is not owned by this plugin") return state, entry - def _baseline_content(self, app_id: str, entry: Dict[str, Any]) -> str: - if not entry.get("override_existed"): - return "" - path = self.flatpak_service._backup_path(app_id) - if path.is_symlink() or not path.is_file(): - raise RuntimeError("Flatpak override backup is unavailable") - return path.read_text(encoding="utf-8") - @staticmethod def _environment_value(content: str, key: str) -> str: section = None @@ -87,31 +74,14 @@ class FlatpakProfileService: return value return "" - @classmethod - def _dxvk_config(cls, baseline: str, frame_rate: int) -> str: - existing = cls._environment_value(baseline, "DXVK_CONFIG") - parts = [part.strip() for part in existing.split(";") if part.strip()] - parts = [part for part in parts if not cls.DXVK_FRAME_RATE_SEGMENT.match(part)] - if frame_rate > 0: - parts.append(f"dxvk.maxFrameRate = {frame_rate}") - return "; ".join(parts) - - def _restore_baseline(self, app_id: str, entry: Dict[str, Any]) -> None: - existed, current = self.flatpak_service._snapshot_override(app_id) - current_hash = self.flatpak_service._sha256(current) if existed else self.flatpak_service._sha256(b"") - if current_hash != entry.get("managed_sha256"): - raise RuntimeError("Flatpak override changed after preparation; refusing to overwrite unrelated settings") - path = self.flatpak_service._override_path(app_id) - if entry.get("override_existed"): - self.flatpak_service._write_file(path, self._baseline_content(app_id, entry)) - else: - path.unlink(missing_ok=True) + @staticmethod + def _dxvk_config(frame_rate: int) -> str: + return f"dxvk.maxFrameRate = {frame_rate}" if frame_rate > 0 else "" def _apply_state(self, app_id: str, workaround_state: Dict[str, Any]) -> Dict[str, Any]: workaround_state = self._validate_state(workaround_state) - _, entry = self._state_entry(app_id) - baseline = self._baseline_content(app_id, entry) - self._restore_baseline(app_id, entry) + self._state_entry(app_id) + self.flatpak_service._reset_app_override(app_id) prepared = self.flatpak_service.prepare_app(app_id) if not prepared.get("success") or not prepared.get("owned"): raise RuntimeError(prepared.get("error") or "Could not restore plugin-owned Flatpak preparation") @@ -137,21 +107,17 @@ class FlatpakProfileService: "--env=MESA_LOADER_DRIVER_OVERRIDE=zink", "--env=GALLIUM_DRIVER=zink", ]) - dxvk_config = self._dxvk_config(baseline, workaround_state["dxvkFrameRate"]) + dxvk_config = self._dxvk_config(workaround_state["dxvkFrameRate"]) if dxvk_config: args.append(f"--env=DXVK_CONFIG={dxvk_config}") args.append(app_id) result = self.flatpak_service._run_flatpak_command(args, capture_output=True, text=True) if result.returncode != 0: raise OSError(result.stderr.strip() or f"Could not apply Flatpak workarounds for {app_id}") - existed, managed = self.flatpak_service._snapshot_override(app_id) - if not existed: - raise RuntimeError(f"Flatpak override for {app_id} was not created") state = self.flatpak_service._read_state() entry = state["prepared_apps"].get(app_id) if not isinstance(entry, dict): raise RuntimeError("Flatpak application ownership state disappeared") - entry["managed_sha256"] = self.flatpak_service._sha256(managed) entry["workaround_state"] = workaround_state self.flatpak_service._write_state(state) return workaround_state @@ -345,15 +311,13 @@ class FlatpakProfileService: config = self.configuration_service.get_flatpak_config(app_id) if not config.get("exists"): continue - existed, content = self.flatpak_service._snapshot_override(app_id) - if not existed or self.flatpak_service._sha256(content) != entry.get("managed_sha256"): - continue + shown = self.flatpak_service._run_flatpak_command( + ["override", "--user", "--show", app_id], + capture_output=True, + text=True, + ) profile = self.configuration_service.flatpak_profile_name(app_id) - try: - text = content.decode("utf-8") - except UnicodeDecodeError: - continue - if self._environment_value(text, "LSFGVK_PROFILE") == profile: + if shown.returncode == 0 and self._environment_value(shown.stdout, "LSFGVK_PROFILE") == profile: enabled.add(app_id) if not enabled: return {"success": True, "message": "", "error": None, "apps": []} diff --git a/py_modules/lsfg_vk/flatpak_service.py b/py_modules/lsfg_vk/flatpak_service.py index f2ed90c..200a998 100644 --- a/py_modules/lsfg_vk/flatpak_service.py +++ b/py_modules/lsfg_vk/flatpak_service.py @@ -1,6 +1,5 @@ from __future__ import annotations -import hashlib import json import os import pwd @@ -36,10 +35,6 @@ class FlatpakService(BaseService): def ownership_path(self) -> Path: return self.config_dir / self.OWNERSHIP_FILENAME - @property - def backup_dir(self) -> Path: - return self.config_dir / "flatpak-overrides" - def _clean_env(self) -> Dict[str, str]: env = os.environ.copy() env.pop("LD_LIBRARY_PATH", None) @@ -77,6 +72,7 @@ class FlatpakService(BaseService): if runuser is None: raise FileNotFoundError("runuser command not available") command = [runuser, "--user", user.pw_name, "--", *command] + kwargs.setdefault("timeout", 300 if args and args[0] in ("install", "uninstall") else 30) return subprocess.run(command, env=env, **kwargs) @classmethod @@ -170,10 +166,6 @@ class FlatpakService(BaseService): self._validate_app_id(app_id) if not isinstance(entry, dict): raise RuntimeError("Invalid Flatpak app ownership metadata") - if type(entry.get("override_existed")) is not bool: - raise RuntimeError("Invalid Flatpak app ownership metadata") - if not isinstance(entry.get("managed_sha256"), str): - raise RuntimeError("Invalid Flatpak app ownership metadata") return data def _write_state(self, state: Dict[str, object]) -> None: @@ -181,8 +173,6 @@ class FlatpakService(BaseService): apps = state.get("prepared_apps", {}) if not branches and not apps: self.ownership_path.unlink(missing_ok=True) - if self.backup_dir.exists() and not any(self.backup_dir.iterdir()): - self.backup_dir.rmdir() return self._write_file( self.ownership_path, @@ -196,12 +186,14 @@ class FlatpakService(BaseService): def _override_path(self, app_id: str) -> Path: return self.user_home / ".local/share/flatpak/overrides" / self._validate_app_id(app_id) - def _backup_path(self, app_id: str) -> Path: - return self.backup_dir / f"{self._validate_app_id(app_id)}.ini" - - @staticmethod - def _sha256(content: bytes) -> str: - return hashlib.sha256(content).hexdigest() + def _reset_app_override(self, app_id: str) -> None: + result = self._run_flatpak_command( + ["override", "--user", "--reset", self._validate_app_id(app_id)], + capture_output=True, + text=True, + ) + if result.returncode != 0: + raise OSError(result.stderr.strip() or f"Could not reset Flatpak override for {app_id}") @staticmethod def _parse_process_start_time(stat_content: str) -> Optional[int]: @@ -226,16 +218,6 @@ class FlatpakService(BaseService): return None return cls._parse_process_start_time(stat_content) - def _snapshot_override(self, app_id: str) -> tuple[bool, bytes]: - path = self._override_path(app_id) - if path.is_symlink(): - raise RuntimeError("Flatpak override path is a symlink") - if not path.exists(): - return False, b"" - if not path.is_file(): - raise RuntimeError("Flatpak override path is not a regular file") - return True, path.read_bytes() - def _resolve_runtime(self, app_id: str) -> tuple[str, str]: self._validate_app_id(app_id) if not self.check_flatpak_available(): @@ -517,28 +499,9 @@ class FlatpakService(BaseService): raise RuntimeError(extension.get("error") or f"Could not install Flatpak runtime {branch}") state = self._read_state() apps = state["prepared_apps"] - status = self._app_override_status(app_id) - if status["prepared"] and app_id not in apps: - return self._success_response( - dict, - "Flatpak application is already prepared outside this plugin", - app_id=app_id, - runtime=runtime, - runtime_branch=branch, - prepared=True, - owned=False, - ) if app_id not in apps: - existed, original = self._snapshot_override(app_id) - backup = self._backup_path(app_id) - if existed: - self._write_file(backup, original.decode("utf-8")) - else: - backup.unlink(missing_ok=True) - apps[app_id] = { - "override_existed": existed, - "managed_sha256": "", - } + self._reset_app_override(app_id) + apps[app_id] = {} result = self._run_flatpak_command( [ "override", @@ -556,13 +519,8 @@ class FlatpakService(BaseService): ) if result.returncode != 0: raise OSError(result.stderr.strip() or f"Could not prepare Flatpak app {app_id}") - status = self._app_override_status(app_id) - if not status["prepared"]: + if not self._app_override_status(app_id)["prepared"]: raise RuntimeError(f"Flatpak preparation did not become visible for {app_id}") - existed, managed = self._snapshot_override(app_id) - if not existed: - raise RuntimeError(f"Flatpak override for {app_id} was not created") - apps[app_id]["managed_sha256"] = self._sha256(managed) self._write_state(state) return self._success_response( dict, @@ -582,8 +540,7 @@ class FlatpakService(BaseService): with self._lock: state = self._read_state() apps = state["prepared_apps"] - entry = apps.get(app_id) - if entry is None: + if app_id not in apps: return self._success_response( dict, "Flatpak application is not plugin-owned; existing overrides were preserved", @@ -591,26 +548,12 @@ class FlatpakService(BaseService): prepared=self._app_override_status(app_id)["prepared"], owned=False, ) - existed, current = self._snapshot_override(app_id) - current_hash = self._sha256(current) if existed else self._sha256(b"") - if current_hash != entry["managed_sha256"]: - raise RuntimeError( - "Flatpak override changed after preparation; refusing to overwrite unrelated settings" - ) - override_path = self._override_path(app_id) - backup_path = self._backup_path(app_id) - if entry["override_existed"]: - if not backup_path.is_file() or backup_path.is_symlink(): - raise RuntimeError("Flatpak override backup is unavailable") - self._write_file(override_path, backup_path.read_text(encoding="utf-8")) - else: - override_path.unlink(missing_ok=True) - backup_path.unlink(missing_ok=True) + self._reset_app_override(app_id) apps.pop(app_id, None) self._write_state(state) return self._success_response( dict, - "Plugin-owned Flatpak preparation removed", + "Plugin-owned Flatpak override reset", app_id=app_id, prepared=False, owned=False, diff --git a/py_modules/lsfg_vk/plugin.py b/py_modules/lsfg_vk/plugin.py index c63e3a9..eed7fd7 100644 --- a/py_modules/lsfg_vk/plugin.py +++ b/py_modules/lsfg_vk/plugin.py @@ -219,4 +219,4 @@ class Plugin: os.path.join(decky.DECKY_HOME, "lossless-scaling-vk"), os.path.join(decky.DECKY_USER_HOME, ".local", "share", "decky-lossless-scaling-vk"), ) - decky.logger.info("decky-lsfg-vk plugin migrations completed") + decky.logger.info("decky-lsfg-vk plugin migrations completed")
\ No newline at end of file diff --git a/tests/test_flatpak_profile_service.py b/tests/test_flatpak_profile_service.py index 2b9933b..e10d3fa 100644 --- a/tests/test_flatpak_profile_service.py +++ b/tests/test_flatpak_profile_service.py @@ -1,4 +1,3 @@ -import hashlib import sys import tempfile import types @@ -23,7 +22,6 @@ class FakeFlatpakService: self.user_home = home self.config_dir = home / ".config/lsfg-vk" self.config_file_path = self.config_dir / "conf.toml" - self.backup_dir = self.config_dir / "flatpak-overrides" self.state = {"version": 2, "plugin_owned_branches": [], "prepared_apps": {}} self.commands = [] self.running = "" @@ -38,18 +36,8 @@ class FakeFlatpakService: def _override_path(self, app_id): return self.user_home / ".local/share/flatpak/overrides" / app_id - def _backup_path(self, app_id): - return self.backup_dir / f"{app_id}.ini" - - @staticmethod - def _sha256(content): - return hashlib.sha256(content).hexdigest() - - def _snapshot_override(self, app_id): - path = self._override_path(app_id) - if not path.exists(): - return False, b"" - return True, path.read_bytes() + def _reset_app_override(self, app_id): + self._override_path(app_id).unlink(missing_ok=True) def _process_start_time(self, pid): return self.start_times.get(pid) @@ -63,34 +51,26 @@ class FakeFlatpakService: def prepare_app(self, app_id): apps = self.state["prepared_apps"] if app_id not in apps: - existed, original = self._snapshot_override(app_id) - if existed: - self._write_file(self._backup_path(app_id), original.decode("utf-8")) - apps[app_id] = {"override_existed": existed, "managed_sha256": ""} - entry = apps[app_id] - baseline = "" - if entry["override_existed"]: - baseline = self._backup_path(app_id).read_text(encoding="utf-8") - managed = baseline + "\n[Context]\nfilesystems=/config:ro;/dll:ro;\n[Environment]\nLSFGVK_CONFIG=/config/conf.toml\nLSFGVK_FLATPAK=1\n" - self._write_file(self._override_path(app_id), managed) - entry["managed_sha256"] = self._sha256(managed.encode()) - return {"success": True, "owned": True, "prepared": True, "runtime": "org.freedesktop.Platform/x86_64/24.08", "runtime_branch": "24.08"} + self._reset_app_override(app_id) + apps[app_id] = {} + self._write_file( + self._override_path(app_id), + "[Context]\nfilesystems=/config:ro;/dll:ro;\n" + "unset-environment=DISABLE_LSFGVK;DISABLE_LSFG;\n" + "[Environment]\nLSFGVK_CONFIG=/config/conf.toml\nLSFGVK_FLATPAK=1\n", + ) + return { + "success": True, + "owned": True, + "prepared": True, + "runtime": "org.freedesktop.Platform/x86_64/24.08", + "runtime_branch": "24.08", + } def remove_app_override(self, app_id): - entry = self.state["prepared_apps"].get(app_id) - if entry is None: + if app_id not in self.state["prepared_apps"]: return {"success": True, "prepared": False, "owned": False} - existed, current = self._snapshot_override(app_id) - current_hash = self._sha256(current) if existed else self._sha256(b"") - if current_hash != entry["managed_sha256"]: - return {"success": False, "error": "Flatpak override changed after preparation"} - path = self._override_path(app_id) - backup = self._backup_path(app_id) - if entry["override_existed"]: - self._write_file(path, backup.read_text(encoding="utf-8")) - else: - path.unlink(missing_ok=True) - backup.unlink(missing_ok=True) + self._reset_app_override(app_id) self.state["prepared_apps"].pop(app_id) return {"success": True, "prepared": False, "owned": False} @@ -114,7 +94,14 @@ class FakeFlatpakService: self.commands.append(args) if args[:3] == ["override", "--user", "--show"]: path = self._override_path(args[3]) - return types.SimpleNamespace(returncode=0, stdout=path.read_text(encoding="utf-8") if path.exists() else "", stderr="") + return types.SimpleNamespace( + returncode=0, + stdout=path.read_text(encoding="utf-8") if path.exists() else "", + stderr="", + ) + if args[:3] == ["override", "--user", "--reset"]: + self._reset_app_override(args[3]) + return types.SimpleNamespace(returncode=0, stdout="", stderr="") if args[0] == "override": app_id = args[-1] path = self._override_path(app_id) @@ -162,9 +149,11 @@ class FlatpakProfileServiceTests(unittest.TestCase): self.assertIn("ENABLE_GAMESCOPE_WSI=0", content) self.assertIn("DXVK_HDR=0", content) - def test_workaround_update_rebuilds_from_original_override(self): - baseline = "[Environment]\nDXVK_CONFIG=dxgi.syncInterval = 0\nKEEP=yes\n" - self.flatpak._write_file(self.flatpak._override_path(self.app_id), baseline) + def test_workaround_update_rebuilds_from_clean_override(self): + self.flatpak._write_file( + self.flatpak._override_path(self.app_id), + "[Environment]\nDXVK_CONFIG=dxgi.syncInterval = 0\nKEEP=yes\n", + ) self.assertTrue(self.service.enable_app(self.app_id)["success"]) state = self.service.default_state() @@ -176,7 +165,8 @@ class FlatpakProfileServiceTests(unittest.TestCase): ) self.assertTrue(result["success"]) - self.assertIn("--env=DXVK_CONFIG=dxgi.syncInterval = 0; dxvk.maxFrameRate = 30", command) + self.assertIn("--env=DXVK_CONFIG=dxvk.maxFrameRate = 30", command) + self.assertNotIn("dxgi.syncInterval", " ".join(command)) self.assertNotIn("--env=DXVK_HDR=0", command) self.assertIn("--env=MESA_LOADER_DRIVER_OVERRIDE=zink", command) @@ -191,26 +181,25 @@ class FlatpakProfileServiceTests(unittest.TestCase): self.assertEqual(result["config"]["multiplier"], 4) self.service.get_app.assert_not_called() - def test_remove_restores_exact_original_override_and_profile(self): - baseline = "[Environment]\nKEEP=yes\n" - self.flatpak._write_file(self.flatpak._override_path(self.app_id), baseline) + def test_remove_resets_override_and_profile(self): + self.flatpak._write_file(self.flatpak._override_path(self.app_id), "[Environment]\nKEEP=yes\n") self.assertTrue(self.service.enable_app(self.app_id)["success"]) removed = self.service.remove_app(self.app_id) self.assertTrue(removed["success"]) - self.assertEqual(self.flatpak._override_path(self.app_id).read_text(encoding="utf-8"), baseline) + self.assertFalse(self.flatpak._override_path(self.app_id).exists()) self.assertFalse(self.configuration.get_flatpak_config(self.app_id)["exists"]) - def test_external_override_change_fails_closed(self): + def test_external_override_change_is_replaced_on_update(self): self.assertTrue(self.service.enable_app(self.app_id)["success"]) path = self.flatpak._override_path(self.app_id) path.write_text(path.read_text(encoding="utf-8") + "EXTERNAL=yes\n", encoding="utf-8") result = self.service.set_workaround_state(self.app_id, self.service.default_state()) - self.assertFalse(result["success"]) - self.assertIn("changed after preparation", result["error"]) + self.assertTrue(result["success"]) + self.assertNotIn("EXTERNAL=yes", path.read_text(encoding="utf-8")) def test_running_detection_uses_owned_selector_state(self): self.assertTrue(self.service.enable_app(self.app_id)["success"]) diff --git a/tests/test_flatpak_service.py b/tests/test_flatpak_service.py index 17d0016..d1ddd0c 100644 --- a/tests/test_flatpak_service.py +++ b/tests/test_flatpak_service.py @@ -134,6 +134,9 @@ class FlatpakServiceTests(unittest.TestCase): if args[:3] == ["override", "--user", "--show"]: path = self.service._override_path(args[-1]) return self._result(path.read_text(encoding="utf-8") if path.exists() else "") + if args[:3] == ["override", "--user", "--reset"]: + self.service._override_path(args[-1]).unlink(missing_ok=True) + return self._result() if args[:2] == ["override", "--user"]: return self._apply_override(args) raise AssertionError(f"Unexpected Flatpak command: {args}") @@ -189,7 +192,7 @@ class FlatpakServiceTests(unittest.TestCase): self.assertNotIn("ENABLE_GAMESCOPE_WSI", content) state = json.loads(self.service.ownership_path.read_text(encoding="utf-8")) self.assertEqual(state["plugin_owned_branches"], ["24.08"]) - self.assertIn("com.example.Game", state["prepared_apps"]) + self.assertEqual(state["prepared_apps"]["com.example.Game"], {}) def test_prepare_is_idempotent(self): first = self.service.prepare_app("com.example.Game") @@ -230,42 +233,20 @@ class FlatpakServiceTests(unittest.TestCase): self.assertEqual(state["plugin_owned_branches"], []) self.assertIn("com.example.Game", state["prepared_apps"]) - def test_external_preparation_is_preserved(self): - path = self.service._override_path("com.example.Game") - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text( - self._serialize_override( - [str(self.service.config_dir) + ":ro", str(self.dll_dir) + ":ro"], - ["DISABLE_LSFGVK", "DISABLE_LSFG"], - { - "LSFGVK_CONFIG": str(self.service.config_file_path), - "LSFGVK_FLATPAK": "1", - }, - ), - encoding="utf-8", - ) - self.system_branches = {"24.08"} - - response = self.service.prepare_app("com.example.Game") - - self.assertTrue(response["success"]) - self.assertTrue(response["prepared"]) - self.assertFalse(response["owned"]) - self.assertFalse(self.service.ownership_path.exists()) - - def test_remove_restores_exact_previous_override(self): + def test_prepare_resets_existing_override(self): self.system_branches = {"24.08"} - original = "[Context]\nfilesystems=~/Documents;\n\n[Environment]\nFOO=bar\n" path = self.service._override_path("com.example.Game") path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(original, encoding="utf-8") - self.assertTrue(self.service.prepare_app("com.example.Game")["success"]) + path.write_text("[Context]\nfilesystems=~/Documents;\n\n[Environment]\nFOO=bar\n", encoding="utf-8") - response = self.service.remove_app_override("com.example.Game") + response = self.service.prepare_app("com.example.Game") + content = path.read_text(encoding="utf-8") self.assertTrue(response["success"]) - self.assertEqual(path.read_text(encoding="utf-8"), original) - self.assertFalse(self.service.ownership_path.exists()) + self.assertTrue(response["owned"]) + self.assertNotIn("~/Documents", content) + self.assertNotIn("FOO=bar", content) + self.assertIn("LSFGVK_CONFIG=", content) def test_remove_deletes_override_created_by_plugin(self): self.system_branches = {"24.08"} @@ -279,7 +260,7 @@ class FlatpakServiceTests(unittest.TestCase): self.assertFalse(path.exists()) self.assertFalse(self.service.ownership_path.exists()) - def test_remove_fails_closed_after_external_change(self): + def test_remove_resets_external_changes(self): self.system_branches = {"24.08"} self.assertTrue(self.service.prepare_app("com.example.Game")["success"]) path = self.service._override_path("com.example.Game") @@ -288,10 +269,21 @@ class FlatpakServiceTests(unittest.TestCase): response = self.service.remove_app_override("com.example.Game") - self.assertFalse(response["success"]) - self.assertIn("changed after preparation", response["error"]) - self.assertTrue(path.exists()) - self.assertTrue(self.service.ownership_path.exists()) + self.assertTrue(response["success"]) + self.assertFalse(path.exists()) + self.assertFalse(self.service.ownership_path.exists()) + + def test_legacy_ownership_fields_are_accepted(self): + self.service.ownership_path.write_text( + json.dumps({ + "version": 2, + "plugin_owned_branches": [], + "prepared_apps": {"com.example.Game": {"override_existed": True, "managed_sha256": "old"}}, + }), + encoding="utf-8", + ) + + self.assertIn("com.example.Game", self.service._read_state()["prepared_apps"]) def test_full_cleanup_removes_only_owned_state(self): self.system_branches = {"23.08"} |
