"""Test configure_hibernate_delay — the suspend-to-hibernate handoff. The desktop panel can upgrade its idle suspend stage to suspend-then-hibernate, but the delay between the two is systemd's HibernateDelaySec, which lives in a root-owned drop-in and so cannot be a panel setting. Left unset the delay depends on the hardware: a machine with a battery hibernates on a low-battery alarm at an hour nobody chose, and one without falls back to systemd's 2h default. 90 minutes is the seeded value: long enough that stepping out of a meeting costs a screen unlock rather than a passphrase and a resume, short enough that a laptop in a bag is not holding its encryption keys in RAM all afternoon (a suspended machine is; a hibernated one is not). Method: sed-extract configure_hibernate_delay from the real `archsetup`, point it at a temp config dir, and assert on the file it writes. python3 -m unittest tests.installer-steps.test_configure_hibernate_delay """ import os import re import stat import subprocess import tempfile import textwrap import unittest REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) ARCHSETUP = os.path.join(REPO_ROOT, "archsetup") def run(confdir): script = textwrap.dedent(f"""\ logfile=/dev/null action="" display() {{ :; }} error_warn() {{ echo "WARN: $1"; return 1; }} source <(sed -n '/^configure_hibernate_delay() {{/,/^}}/p' "{ARCHSETUP}") configure_hibernate_delay "{confdir}" echo "RC=$?" exit 0 """) return subprocess.run( ["bash", "-c", script], capture_output=True, text=True, timeout=10, ) def rc_of(r): m = re.search(r"^RC=(\d+)$", r.stdout, re.M) assert m, "no RC line in output: %r / %r" % (r.stdout, r.stderr) return int(m.group(1)) def body(confdir): with open(os.path.join(confdir, "60-hibernate-delay.conf")) as f: return f.read() class ConfigureHibernateDelay(unittest.TestCase): # ------------------------------------------------------------ normal ---- def test_writes_a_sleep_drop_in_with_the_delay(self): with tempfile.TemporaryDirectory() as d: r = run(d) self.assertEqual(rc_of(r), 0) text = body(d) self.assertIn("[Sleep]", text) self.assertIn("HibernateDelaySec=90min", text) def test_drop_in_is_world_readable_not_writable(self): with tempfile.TemporaryDirectory() as d: run(d) mode = stat.S_IMODE( os.stat(os.path.join(d, "60-hibernate-delay.conf")).st_mode) self.assertEqual(mode, 0o644) def test_explains_why_the_value_is_not_left_to_systemd(self): # The number is a judgement call, so the file has to carry its # reasoning: whoever changes it next needs the tradeoff, not just # a number to overwrite. Assert on the tradeoff's own terms — # "contains a hash" would pass on any comment at all. with tempfile.TemporaryDirectory() as d: run(d) text = body(d).lower() self.assertIn("2h default", text) # what unset would give self.assertIn("encryption keys", text) # why not longer self.assertIn("passphrase", text) # why not shorter def test_drop_in_sorts_above_vendor_drop_ins(self): # systemd reserves 10-40 for /usr and 60-90 for /etc. A 10- file # in /etc sorts below a vendor 20- file and silently loses to it. with tempfile.TemporaryDirectory() as d: run(d) name = os.listdir(d)[0] prefix = int(name.split("-")[0]) self.assertGreaterEqual(prefix, 60) self.assertLessEqual(prefix, 90) # ---------------------------------------------------------- boundary ---- def test_running_twice_leaves_one_correct_drop_in(self): with tempfile.TemporaryDirectory() as d: run(d) first = body(d) r = run(d) self.assertEqual(rc_of(r), 0) self.assertEqual(first, body(d)) def test_absent_directory_is_created(self): with tempfile.TemporaryDirectory() as d: nested = os.path.join(d, "etc", "systemd", "sleep.conf.d") r = run(nested) self.assertEqual(rc_of(r), 0) self.assertIn("HibernateDelaySec=90min", body(nested)) # ------------------------------------------------------------- error ---- @unittest.skipUnless(os.geteuid() != 0, "root ignores directory write bits") def test_unwritable_directory_warns_and_does_not_crash(self): with tempfile.TemporaryDirectory() as d: confdir = os.path.join(d, "ro") os.mkdir(confdir) os.chmod(confdir, 0o500) try: r = run(confdir) self.assertIn("WARN:", r.stdout) self.assertNotEqual(rc_of(r), 0) finally: os.chmod(confdir, 0o700) if __name__ == "__main__": unittest.main()