mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-29 04:01:26 +01:00
Sinceb61b4d6, <file>.ft-bak is written only before the first repair. That keeps the first config, but no repair after it had a backup, and the log still said "(backup: <file>.ft-bak)" for each one. The repair writes 0 over a moved panel's old screen number, so a backup is the only record of where the panel was. Restoring the file the log named after a later repair also threw away every Plasma change since the first one: panels, widgets, pinned apps, wallpapers. Repeated repairs are the caseb61b4d6was written for. Tried on a copy: after a first repair, a new top panel on screen 2 with pinned apps, and a drop to one screen, the second repair moved that panel and named a backup that had neither. Before each repair the config now also goes to <file>.ft-bak.last (whole or not at all, like <file>.ft-bak). <file>.ft-bak stays write-once. The log names both, and the docstring and docs/design.md describe both. Both live in ~/.config/frametop, which uninstall.sh already deletes with the settings. The new test repairs twice, with a widget changed between the runs, and checks that .last holds the changed config, .ft-bak the first one, and the log names both. test_repair checks that the first repair writes both and a run with nothing to repair writes neither. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
224 lines
8.5 KiB
Python
224 lines
8.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Tests for session/fix-panels.py: issue #18's config (the taskbar saved on spare output 8
|
|
of a 3-screen desktop), panels that are fine, an edge that's taken, and a real run of
|
|
kwriteconfig6 on a copy. Nothing here touches the running desktop or its config.
|
|
|
|
python3 session/tests/test_fix_panels.py
|
|
"""
|
|
import importlib.util
|
|
import os
|
|
import resource
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
HERE = os.path.dirname(os.path.realpath(__file__))
|
|
SCRIPT = os.path.join(HERE, "..", "fix-panels.py")
|
|
spec = importlib.util.spec_from_file_location("fix_panels", SCRIPT)
|
|
fp = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(fp)
|
|
|
|
|
|
def desktop(cid, screen):
|
|
return f"""[Containments][{cid}]
|
|
activityId=7e1d1a3c-0000-4000-8000-000000000000
|
|
formfactor=0
|
|
immutability=1
|
|
lastScreen={screen}
|
|
location=0
|
|
plugin=org.kde.plasma.folder
|
|
wallpaperplugin=org.kde.image
|
|
"""
|
|
|
|
|
|
def panel(cid, screen, location=4, tray=None):
|
|
text = f"""[Containments][{cid}]
|
|
activityId=
|
|
formfactor=2
|
|
immutability=1
|
|
lastScreen={screen}
|
|
location={location}
|
|
plugin=org.kde.panel
|
|
wallpaperplugin=org.kde.image
|
|
|
|
[Containments][{cid}][Applets][{cid}1]
|
|
immutability=1
|
|
plugin=org.kde.plasma.kickoff
|
|
"""
|
|
if tray:
|
|
text += f"""
|
|
[Containments][{cid}][Applets][{cid}2]
|
|
immutability=1
|
|
plugin=org.kde.plasma.systemtray
|
|
|
|
[Containments][{cid}][Applets][{cid}2][Configuration]
|
|
PreloadWeight=90
|
|
SystrayContainmentId={tray}
|
|
|
|
[Containments][{tray}]
|
|
activityId=
|
|
formfactor=2
|
|
immutability=1
|
|
lastScreen={screen}
|
|
location={location}
|
|
plugin=org.kde.plasma.private.systemtray
|
|
popupHeight=432
|
|
"""
|
|
return text
|
|
|
|
|
|
ISSUE_18 = "\n".join([desktop(1, 0), desktop(18, 1), desktop(19, 2)] + [desktop(20 + i, 3 + i) for i in range(8)]
|
|
+ [panel(10, 8, tray=11), "[ScreenMapping]\nitemsOnDisabledScreens=\n"])
|
|
|
|
|
|
class Plan(unittest.TestCase):
|
|
def test_issue_18(self):
|
|
moves, kept = fp.plan(fp.parse(ISSUE_18), 3)
|
|
self.assertEqual(moves, [("10", 8, ["10", "11"])])
|
|
self.assertEqual(kept, [])
|
|
|
|
def test_panel_on_a_screen_stays(self):
|
|
for screen in (0, 2, -1):
|
|
moves, kept = fp.plan(fp.parse(desktop(1, 0) + panel(2, screen, tray=8)), 3)
|
|
self.assertEqual((moves, kept), ([], []), screen)
|
|
|
|
def test_tray_is_not_a_panel(self):
|
|
found = fp.panels(fp.parse(panel(2, 0, tray=8)))
|
|
self.assertEqual(list(found), ["2"])
|
|
self.assertEqual(found["2"]["trays"], ["8"])
|
|
|
|
def test_desktops_never_move(self):
|
|
moves, _ = fp.plan(fp.parse(ISSUE_18), 1)
|
|
self.assertEqual([m[0] for m in moves], ["10"])
|
|
|
|
def test_edge_taken_on_first_screen(self):
|
|
text = panel(2, 0, tray=8) + panel(10, 8, tray=11)
|
|
moves, kept = fp.plan(fp.parse(text), 3)
|
|
self.assertEqual(moves, [])
|
|
self.assertEqual([k[:2] for k in kept], [("10", 8)])
|
|
|
|
def test_other_edge_is_free(self):
|
|
moves, kept = fp.plan(fp.parse(panel(2, 0) + panel(10, 4, location=3)), 3)
|
|
self.assertEqual(moves, [("10", 4, ["10"])])
|
|
self.assertEqual(kept, [])
|
|
|
|
def test_two_lost_on_one_edge(self):
|
|
moves, kept = fp.plan(fp.parse(panel(10, 5) + panel(12, 8)), 3)
|
|
self.assertEqual([m[0] for m in moves], ["10"])
|
|
self.assertEqual([k[0] for k in kept], ["12"])
|
|
|
|
def test_screen_count_went_down(self):
|
|
moves, _ = fp.plan(fp.parse(panel(2, 1, location=3)), 1)
|
|
self.assertEqual(moves, [("2", 1, ["2"])])
|
|
|
|
|
|
class Run(unittest.TestCase):
|
|
def setUp(self):
|
|
self.dir = tempfile.TemporaryDirectory()
|
|
self.path = os.path.join(self.dir.name, "plasma-org.kde.plasma.desktop-appletsrc")
|
|
with open(self.path, "w") as f:
|
|
f.write(ISSUE_18)
|
|
|
|
def tearDown(self):
|
|
self.dir.cleanup()
|
|
|
|
def run_script(self, *args):
|
|
return subprocess.run([sys.executable, SCRIPT, "--file", self.path, "--screens", "3", *args],
|
|
capture_output=True, text=True)
|
|
|
|
def test_check_reports_and_changes_nothing(self):
|
|
r = self.run_script("--check")
|
|
self.assertEqual(r.returncode, 1)
|
|
self.assertIn("panel 10: screen 8, bottom (lost", r.stdout)
|
|
with open(self.path) as f:
|
|
self.assertEqual(f.read(), ISSUE_18)
|
|
|
|
def test_repair(self):
|
|
r = self.run_script()
|
|
self.assertEqual(r.returncode, 0, r.stderr)
|
|
self.assertIn("moved it to the first screen", r.stderr)
|
|
with open(self.path) as f:
|
|
groups = fp.parse(f.read())
|
|
self.assertEqual(groups[("Containments", "10")]["lastScreen"], "0")
|
|
self.assertEqual(groups[("Containments", "11")]["lastScreen"], "0")
|
|
self.assertEqual(groups[("Containments", "10", "Applets", "101")]["plugin"], "org.kde.plasma.kickoff")
|
|
self.assertEqual(groups[("Containments", "25")]["lastScreen"], "8") # a spare's desktop
|
|
for bak in (".ft-bak", ".ft-bak.last"):
|
|
with open(self.path + bak) as f:
|
|
self.assertEqual(f.read(), ISSUE_18, bak)
|
|
# A second run finds nothing to do and makes no backup.
|
|
os.remove(self.path + ".ft-bak")
|
|
os.remove(self.path + ".ft-bak.last")
|
|
r = self.run_script()
|
|
self.assertEqual((r.returncode, r.stderr), (0, ""))
|
|
self.assertEqual(os.listdir(self.dir.name), [os.path.basename(self.path)])
|
|
self.assertEqual(self.run_script("--check").returncode, 0)
|
|
|
|
def test_no_config_yet(self):
|
|
os.remove(self.path)
|
|
self.assertEqual(self.run_script().returncode, 0)
|
|
self.assertEqual(self.run_script("--check").returncode, 0)
|
|
|
|
def test_backup_is_written_once(self):
|
|
"""A backup that's already there, the config before an earlier repair, stays as it
|
|
was, and the repair still goes ahead."""
|
|
with open(self.path + ".ft-bak", "w") as f:
|
|
f.write("the config before an earlier repair\n")
|
|
r = self.run_script()
|
|
self.assertEqual(r.returncode, 0, r.stderr)
|
|
with open(self.path + ".ft-bak") as f:
|
|
self.assertEqual(f.read(), "the config before an earlier repair\n")
|
|
with open(self.path) as f:
|
|
groups = fp.parse(f.read())
|
|
self.assertEqual(groups[("Containments", "10")]["lastScreen"], "0")
|
|
self.assertEqual(groups[("Containments", "11")]["lastScreen"], "0")
|
|
|
|
def test_last_backup_is_from_before_each_repair(self):
|
|
"""<file>.ft-bak.last is the config as it was just before the latest repair, so that
|
|
repair can be undone without losing what changed since the first; <file>.ft-bak stays
|
|
the config before the first repair. The log names both."""
|
|
bak = self.path + ".ft-bak"
|
|
r = self.run_script()
|
|
self.assertEqual(r.returncode, 0, r.stderr)
|
|
# The panel is lost again later, after a change of the user's own (another widget).
|
|
changed = ISSUE_18.replace("org.kde.plasma.kickoff", "org.kde.plasma.trash")
|
|
with open(self.path, "w") as f:
|
|
f.write(changed)
|
|
r = self.run_script()
|
|
self.assertEqual(r.returncode, 0, r.stderr)
|
|
with open(bak + ".last") as f:
|
|
self.assertEqual(f.read(), changed)
|
|
with open(bak) as f:
|
|
self.assertEqual(f.read(), ISSUE_18)
|
|
with open(self.path) as f:
|
|
self.assertEqual(fp.parse(f.read())[("Containments", "10")]["lastScreen"], "0")
|
|
self.assertIn(bak + ".last ", r.stderr)
|
|
self.assertIn(bak + " ", r.stderr)
|
|
|
|
def test_failed_backup_leaves_nothing(self):
|
|
"""A backup cut off partway (a full disk; here a 1 KiB limit on file size) leaves no
|
|
backup and no temporary file behind, and the panels aren't touched without one. The
|
|
next start, with room, makes the backup and repairs."""
|
|
self.assertGreater(len(ISSUE_18), 1024)
|
|
|
|
def small_files():
|
|
resource.setrlimit(resource.RLIMIT_FSIZE, (1024, 1024))
|
|
r = subprocess.run([sys.executable, SCRIPT, "--file", self.path, "--screens", "3"],
|
|
capture_output=True, text=True, preexec_fn=small_files)
|
|
self.assertEqual(os.listdir(self.dir.name), [os.path.basename(self.path)])
|
|
self.assertEqual(r.returncode, 1, r.stderr)
|
|
self.assertIn("couldn't back up", r.stderr)
|
|
with open(self.path) as f:
|
|
self.assertEqual(f.read(), ISSUE_18)
|
|
r = self.run_script()
|
|
self.assertEqual(r.returncode, 0, r.stderr)
|
|
with open(self.path + ".ft-bak") as f:
|
|
self.assertEqual(f.read(), ISSUE_18)
|
|
with open(self.path) as f:
|
|
self.assertEqual(fp.parse(f.read())[("Containments", "10")]["lastScreen"], "0")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|