summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>2026-09-13 11:53:37 -0400
committerKurt Himebauch <136133082+xXJSONDeruloXx@users.noreply.github.com>2026-09-13 11:53:37 -0400
commitd9c6a48dead8e521912fc64a109e72ec7811082d (patch)
treee98145365c3c178d60434401529734e193bf1f0d
parentdaadf150e7c89a7725045f497328540959433082 (diff)
downloaddecky-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.py60
-rw-r--r--py_modules/lsfg_vk/flatpak_service.py87
-rw-r--r--py_modules/lsfg_vk/plugin.py2
-rw-r--r--tests/test_flatpak_profile_service.py91
-rw-r--r--tests/test_flatpak_service.py64
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"}