1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
"""Tests for configure_hyprlock_pam in the archsetup installer.
hyprlock's packaged /etc/pam.d/hyprlock ships only `auth include login`, with no
account or session sections. pam_end() then crashes cleaning up handles that
were never initialised -- one of the documented causes of the hyprlock lockout
where the session wedges with no prompt (hyprlock#953). configure_hyprlock_pam
writes a complete stack so the account and session phases run.
These tests exercise the REAL function body, extracted from the `archsetup`
script at run time, against a fixture PAM path.
Run from repo root:
python3 -m unittest tests.installer-steps.test_hyprlock_pam
"""
import os
import subprocess
import tempfile
import unittest
REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
ARCHSETUP = os.path.join(REPO_ROOT, "archsetup")
def run(path):
script = (
'logfile=/dev/null\n'
'display() { :; }\n'
'backup_system_file() { :; }\n'
'error_warn() { echo "WARN:$1"; return 1; }\n'
"source <(sed -n '/^configure_hyprlock_pam() {/,/^}/p' \"%s\")\n"
'configure_hyprlock_pam "%s"\n'
) % (ARCHSETUP, path)
return subprocess.run(["bash", "-c", script], capture_output=True,
text=True, timeout=10)
class ConfigureHyprlockPam(unittest.TestCase):
def _write(self, initial="auth include login\n"):
fd, path = tempfile.mkstemp(prefix="pam-hyprlock-")
os.close(fd)
with open(path, "w") as f:
f.write(initial)
self.addCleanup(os.remove, path)
return path
def _stack(self, path):
with open(path) as f:
return [l.split()[0] for l in f if l.strip()
and not l.lstrip().startswith("#")]
# ---------------------------------------------------------- normal ----
def test_writes_all_three_phases(self):
path = self._write()
run(path)
phases = set(self._stack(path))
self.assertEqual(phases, {"auth", "account", "session"},
"the complete stack must carry all three PAM phases")
def test_account_and_session_are_the_added_ones(self):
path = self._write()
run(path)
stack = self._stack(path)
self.assertIn("account", stack)
self.assertIn("session", stack)
# -------------------------------------------------------- boundary ----
def test_idempotent_on_rerun(self):
path = self._write()
run(path)
first = open(path).read()
run(path)
self.assertEqual(open(path).read(), first,
"a second run must not stack duplicate lines")
def test_overwrites_the_incomplete_package_default(self):
# The package default is exactly `auth include login`; a re-run replaces
# it wholesale rather than appending to it.
path = self._write("auth include login\n")
run(path)
self.assertEqual(self._stack(path).count("auth"), 1)
if __name__ == "__main__":
unittest.main()
|