Files
DroidDeck/tools/tests/test_flatpak_uninstall.py

124 lines
4.6 KiB
Python

import importlib.machinery
import importlib.util
import io
import json
import os
from pathlib import Path
import shutil
import sys
import tempfile
import types
import unittest
from contextlib import redirect_stdout
SCRIPT = Path(__file__).resolve().parents[1] / "linuxfs/overlay/usr/local/bin/droiddeck-flatpak"
class FakeInstallation:
def __init__(self, root):
self.root = root
def get_path(self):
return types.SimpleNamespace(get_path=lambda: self.root)
def load(root, ref, transactions):
repository = types.ModuleType("gi.repository")
repository.Flatpak = types.SimpleNamespace()
repository.Gio = types.SimpleNamespace()
repository.GLib = types.SimpleNamespace(Error=RuntimeError)
gi = types.ModuleType("gi")
gi.require_version = lambda *args: None
gi.repository = repository
saved = {name: sys.modules.get(name) for name in ("gi", "gi.repository")}
sys.modules["gi"] = gi
sys.modules["gi.repository"] = repository
try:
loader = importlib.machinery.SourceFileLoader("droiddeck_flatpak_uninstall", str(SCRIPT))
module = importlib.util.module_from_spec(importlib.util.spec_from_loader(loader.name, loader))
loader.exec_module(module)
finally:
for name, value in saved.items():
if value is None:
sys.modules.pop(name, None)
else:
sys.modules[name] = value
module.installation = lambda: FakeInstallation(root)
module.app_ref = lambda inst, app_id: ref
def transaction(inst):
transactions.append(inst)
raise AssertionError("an uninstalled app reached a transaction")
module.transaction = transaction
return module
class FlatpakUninstallTest(unittest.TestCase):
def setUp(self):
self.root = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self.root, True)
self.apps = os.path.join(self.root, "app")
os.makedirs(self.apps)
def uninstall(self, app_id, ref=None):
transactions = []
module = load(self.root, ref, transactions)
out = io.StringIO()
with redirect_stdout(out):
rc = module.uninstall(app_id)
return rc, [json.loads(line) for line in out.getvalue().splitlines()], transactions
def test_an_app_folder_flatpak_no_longer_knows_is_removed(self):
stale = os.path.join(self.apps, "org.videolan.VLC")
os.makedirs(os.path.join(stale, "aarch64", "stable"))
os.symlink("missing-deploy", os.path.join(stale, "aarch64", "stable", "active"))
rc, events, transactions = self.uninstall("org.videolan.VLC")
self.assertEqual(rc, 0)
self.assertFalse(os.path.lexists(stale))
self.assertEqual(events, [{"e": "done", "note": "not installed"}])
self.assertEqual(transactions, [])
def test_other_apps_and_link_targets_are_left_alone(self):
outside = os.path.join(self.root, "outside")
os.makedirs(outside)
keep = os.path.join(outside, "keep")
open(keep, "w").close()
stale = os.path.join(self.apps, "com.google.Chrome")
os.makedirs(stale)
os.symlink(outside, os.path.join(stale, "linked"))
other = os.path.join(self.apps, "org.videolan.VLC")
os.makedirs(other)
rc, _, _ = self.uninstall("com.google.Chrome")
self.assertEqual(rc, 0)
self.assertFalse(os.path.lexists(stale))
self.assertTrue(os.path.isfile(keep))
self.assertTrue(os.path.isdir(other))
def test_a_linked_app_folder_is_not_followed(self):
target = os.path.join(self.root, "elsewhere")
os.makedirs(target)
open(os.path.join(target, "keep"), "w").close()
os.symlink(target, os.path.join(self.apps, "com.example.Linked"))
rc, _, _ = self.uninstall("com.example.Linked")
self.assertEqual(rc, 0)
self.assertTrue(os.path.isfile(os.path.join(target, "keep")))
def test_ids_that_leave_the_app_folder_are_refused(self):
for app_id in ("..", ".", "", "../app", "a/b"):
rc, events, _ = self.uninstall(app_id)
self.assertEqual(rc, 0)
self.assertEqual(events, [{"e": "done", "note": "not installed"}])
self.assertTrue(os.path.isdir(self.apps))
def test_an_installed_app_still_goes_through_flatpak(self):
os.makedirs(os.path.join(self.apps, "org.videolan.VLC"))
ref = types.SimpleNamespace(format_ref=lambda: "app/org.videolan.VLC/aarch64/stable")
with self.assertRaises(AssertionError):
self.uninstall("org.videolan.VLC", ref)
self.assertTrue(os.path.isdir(os.path.join(self.apps, "org.videolan.VLC")))
if __name__ == "__main__":
unittest.main()