aboutsummaryrefslogtreecommitdiff
path: root/tests/installer-steps/test_configure_tlp_power.py
blob: c88e0c2132086bf11e3c948ff55c4dac67133000 (plain)
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"""Test configure_tlp_power's radio-enable line and laptop gating.

systemd-rfkill is masked on laptops because it fights TLP's radio handling —
which means nothing restores radio state at boot unless TLP is told to. The
velox 2026-04-10 setup found wifi and bluetooth soft-blocked on first boot for
exactly this reason. The conf written here must carry
DEVICES_TO_ENABLE_ON_STARTUP so a fresh install comes up with radios on.

Method: sed-extract configure_tlp_power from the real `archsetup`, point it at
a temp tlp.d dir and a temp power-supply dir, and fake pacman_install /
run_task / display / error_warn / systemctl.

Run from repo root:
    python3 -m unittest tests.installer-steps.test_configure_tlp_power
"""

import os
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(battery=True, bat_name="BAT0", unwritable_tlpd=False):
    with tempfile.TemporaryDirectory() as d:
        psdir = os.path.join(d, "power_supply")
        os.makedirs(psdir)
        if battery:
            open(os.path.join(psdir, bat_name), "w").close()
        tlpd = os.path.join(d, "tlp.d")
        if unwritable_tlpd:
            os.makedirs(tlpd)
            os.chmod(tlpd, stat.S_IRUSR | stat.S_IXUSR)
        # The real mask call redirects stdout into $logfile, so the fake
        # systemctl records to a side file the test reads back instead.
        script = textwrap.dedent(f"""\
            logfile=/dev/null
            action=""
            display() {{ :; }}
            pacman_install() {{ echo "INSTALL: $1"; }}
            run_task() {{ echo "TASK: $1"; }}
            systemctl() {{ echo "SYSTEMCTL: $*" >> "{d}/systemctl.log"; }}
            error_warn() {{ echo "WARN: $1"; return 1; }}
            source <(sed -n '/^configure_tlp_power() {{/,/^}}/p' "{ARCHSETUP}")
            configure_tlp_power "{tlpd}" "{psdir}"
            echo "RC=$?"
            echo "CONF:[$(cat "{tlpd}/01-custom.conf" 2>/dev/null)]"
            [ -f "{d}/systemctl.log" ] && cat "{d}/systemctl.log"
            exit 0
        """)
        r = subprocess.run(
            ["bash", "-c", script], capture_output=True, text=True, timeout=10,
        )
        if unwritable_tlpd:
            os.chmod(tlpd, stat.S_IRWXU)
        return r


class ConfigureTlpPower(unittest.TestCase):
    # ------------------------------------------------------------ normal ----
    def test_laptop_gets_tlp_with_radio_enable_line(self):
        r = run(battery=True)
        self.assertIn("INSTALL: tlp", r.stdout)
        conf = r.stdout.split("CONF:[")[1]
        self.assertIn('DEVICES_TO_ENABLE_ON_STARTUP="bluetooth wifi"', conf,
                      "radios must be re-enabled at boot: systemd-rfkill is "
                      "masked, so TLP is the only thing that can do it")
        self.assertIn("CPU_ENERGY_PERF_POLICY_ON_AC", conf)
        self.assertIn("SYSTEMCTL: mask systemd-rfkill.service systemd-rfkill.socket",
                      r.stdout)
        self.assertIn("TASK: enabling TLP service", r.stdout)

    def test_radio_line_is_active_not_commented(self):
        r = run(battery=True)
        conf = r.stdout.split("CONF:[")[1].split("]")[0]
        for line in conf.splitlines():
            if "DEVICES_TO_ENABLE_ON_STARTUP" in line:
                self.assertFalse(line.lstrip().startswith("#"),
                                 "the radio-enable line must not be commented out")
                break
        else:
            self.fail("DEVICES_TO_ENABLE_ON_STARTUP line missing from conf")

    # ---------------------------------------------------------- boundary ----
    def test_desktop_without_battery_is_a_no_op(self):
        r = run(battery=False)
        self.assertNotIn("INSTALL:", r.stdout)
        self.assertNotIn("SYSTEMCTL:", r.stdout)
        self.assertIn("CONF:[]", r.stdout, "no conf may be written on a desktop")

    def test_second_battery_index_still_counts_as_laptop(self):
        r = run(battery=True, bat_name="BAT1")
        self.assertIn("INSTALL: tlp", r.stdout)
        self.assertIn('DEVICES_TO_ENABLE_ON_STARTUP', r.stdout)

    # ------------------------------------------------------------- error ----
    @unittest.skipUnless(os.geteuid() != 0, "root ignores directory write bits")
    def test_unwritable_tlpd_warns_and_does_not_crash(self):
        r = run(battery=True, unwritable_tlpd=True)
        self.assertIn("WARN:", r.stdout,
                      "a failed conf write must surface through error_warn")
        self.assertEqual(r.returncode, 0)


if __name__ == "__main__":
    unittest.main()