Fiw-Gentoo-Dots/tests/test_setup.py

143 lines
8.5 KiB
Python

import importlib.util
import json
import subprocess
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
LIB = Path(__file__).resolve().parents[1] / 'lib'
def module(name):
spec = importlib.util.spec_from_file_location(name, LIB / (name + '.py'))
value = importlib.util.module_from_spec(spec)
spec.loader.exec_module(value)
return value
setup = module('setup')
boot = module('boot')
class SetupTests(unittest.TestCase):
def test_only_selected_overlays_are_staged_and_existing_ones_are_preserved(self):
self.assertEqual(setup.required_repositories({'groups': ['kde']}), [])
self.assertEqual(setup.required_repositories({'groups': ['tidewm']}), ['guru'])
self.assertEqual(setup.required_repositories({'groups': ['gaming']}), ['guru', 'steam-overlay'])
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
def clone(command, **kwargs):
destination = Path(command[-1])
(destination / 'profiles').mkdir(parents=True)
(destination / 'profiles/repo_name').write_text(destination.name + '\n')
return subprocess.CompletedProcess(command, 0)
with patch.object(setup, 'existing_repository', side_effect=lambda name: Path('/existing') if name == 'guru' else None), patch.object(setup.shutil, 'which', return_value='/usr/bin/git'), patch.object(setup.subprocess, 'run', side_effect=clone) as run:
fetched = setup.stage_repositories({'groups': ['gaming']}, root)
self.assertEqual(set(fetched), {'steam-overlay'})
self.assertEqual(run.call_count, 1)
config = root / 'etc/portage/repos.conf/fiw-dots-steam-overlay.conf'
self.assertIn(str(root / 'var/db/repos/steam-overlay'), config.read_text())
self.assertFalse((root / 'etc/portage/repos.conf/fiw-dots-guru.conf').exists())
def test_flatpak_failure_is_reported_and_installs_are_user_scoped(self):
reports = []
installed = set()
def run(command, **kwargs):
if command[:2] == ['flatpak', 'info']:
return subprocess.CompletedProcess(command, 0 if command[-1] in installed else 1)
if 'install' in command and command[-1] == 'org.localsend.localsend_app':
installed.add(command[-1])
return subprocess.CompletedProcess(command, 0)
if 'install' in command:
return subprocess.CompletedProcess(command, 1)
return subprocess.CompletedProcess(command, 0)
with patch.object(setup.os, 'geteuid', return_value=1000), patch.object(setup.shutil, 'which', return_value='/usr/bin/flatpak'), patch.object(setup.subprocess, 'run', side_effect=run) as commands, patch.object(setup, 'write_report', side_effect=lambda kind, report: reports.append(report)):
with self.assertRaises(RuntimeError):
setup.install_flatpaks({'flatpaks': list(setup.FLATPAKS)}, lambda *args: 'install')
self.assertEqual(reports[0]['installed'], ['org.localsend.localsend_app'])
self.assertEqual(reports[0]['failed'], ['org.vinegarhq.Sober'])
for call in commands.call_args_list:
self.assertIn('--user', call.args[0])
def test_missing_services_are_reported_and_enabled_without_starting(self):
reports = []
def run(command, **kwargs):
if 'show' in command:
state = 'not-found\n' if command[-1] == 'wireplumber.service' else 'loaded\n'
return subprocess.CompletedProcess(command, 0, state)
return subprocess.CompletedProcess(command, 0)
with patch.object(setup.os, 'geteuid', return_value=1000), patch.object(setup.subprocess, 'run', side_effect=run) as commands, patch.object(setup, 'write_report', side_effect=lambda kind, report, system: reports.append(report)):
setup.enable_services({'services': ['audio', 'network']}, lambda *args: 'enable')
self.assertEqual(reports[0]['missing'], ['wireplumber.service'])
self.assertEqual(reports[0]['enabled'], ['pipewire.socket', 'pipewire-pulse.socket'])
for call in commands.call_args_list:
self.assertNotIn('--now', call.args[0])
self.assertIn('--user', call.args[0])
class BootTests(unittest.TestCase):
def test_managed_limine_block_preserves_other_entries_and_rejects_bad_markers(self):
original = 'timeout: 8\n\n/Other OS\n protocol: efi\n path: boot():/other.efi\n'
block = boot.render_limine([{'version': 'test-gentoo'}], 'root=UUID=target-root ro')
merged = boot.merge_limine(original, block)
self.assertTrue(merged.startswith(original))
self.assertEqual(boot.merge_limine(merged, block), merged)
with self.assertRaises(RuntimeError):
boot.merge_limine(original + boot.BEGIN, block)
def test_edited_limine_entries_get_proposal_while_external_entries_remain(self):
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
image = root / 'kernel';image.write_bytes(b'kernel')
initrd = root / 'initrd';initrd.write_bytes(b'initrd')
images = [{'version': 'test-gentoo', 'kernel': str(image), 'initrd': str(initrd)}]
saved = {'mode': 'limine', 'esp': str(root / 'esp'), 'cmdline': 'root=UUID=target-root ro'}
(root / 'esp').mkdir()
with patch.object(boot, 'discover_esp', return_value={'path': saved['esp']}), patch.object(boot, 'kernel_images', return_value=images):
saved = boot.refresh(saved, root / 'backups')
config = root / 'esp/EFI/Fiw-Gentoo/limine.conf'
config.write_text(config.read_text() + '\n/Other OS\n protocol: efi\n')
saved = boot.refresh(saved, root / 'backups')
self.assertIn('/Other OS', config.read_text())
config.write_text(config.read_text().replace('target-root', 'user-edited-root'))
previous = config.read_text()
boot.refresh(saved, root / 'backups')
self.assertEqual(config.read_text(), previous)
proposals = list(config.parent.glob('limine.conf.new.*'))
self.assertEqual(len(proposals), 1)
self.assertIn('/Other OS', proposals[0].read_text())
def test_failed_grub_generation_does_not_replace_existing_menu(self):
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)
folder = root / 'boot';(folder / 'grub').mkdir(parents=True)
config = folder / 'grub/grub.cfg';config.write_text('working config\n')
images = []
def run(command, **kwargs):
Path(command[-1]).write_text('partial failed config')
raise subprocess.CalledProcessError(1, command)
# /boot sources prevent normalization writes outside the staging dir.
images = [{'version': 'test', 'kernel': '/boot/kernel-test', 'initrd': '/boot/initramfs-test.img'}]
saved = {'mode': 'grub', 'esp': str(root), 'config_hash': boot.file_hash(config)}
with patch.object(boot, 'GRUB_DIRECTORY', folder), patch.object(boot, 'discover_esp', return_value={'path': str(root)}), patch.object(boot, 'kernel_images', return_value=images), patch.object(boot.subprocess, 'run', side_effect=run):
with self.assertRaises(subprocess.CalledProcessError):
boot.refresh(saved, root / 'backups')
self.assertEqual(config.read_text(), 'working config\n')
self.assertFalse(list(config.parent.glob('.*.tmp')))
def test_firmware_add_preserves_bootorder_and_repeat_default_does_not_duplicate(self):
target = {'disk': '/dev/testdisk', 'partition': 2, 'partuuid': 'target-partition'}
loader = '/EFI/Fiw-Gentoo/BOOTX64.EFI'
command = boot.firmware_commands(target, 'limine', loader, 'add', '')[0]
self.assertIn('--create-only', command)
self.assertNotIn('--create', command)
listing = 'BootOrder: 0001,0002\nBoot0002* Fiw-Gentoo-limine HD(2,GPT,target-partition)/File(\\EFI\\Fiw-Gentoo\\BOOTX64.EFI)\n'
commands = boot.firmware_commands(target, 'limine', loader, 'default', listing)
self.assertEqual(commands, [['efibootmgr', '--bootorder', '0002,0001']])
self.assertEqual(boot.firmware_commands(target, 'limine', loader, 'add', listing), [])
self.assertEqual(boot.plan('keep'), {'mode': 'keep', 'changes': []})
if __name__ == '__main__':
unittest.main()