aboutsummaryrefslogtreecommitdiff
path: root/tests/installer-steps/test_install_cpu_microcode.py
blob: 20f18209e7c1d87f63bf6ec94a14f2f881b0e823 (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 install_cpu_microcode — vendor-detected microcode package install.

archsetup never installed intel-ucode/amd-ucode (found on velox 2026-04-10:
CPU running old microcode, hand-fixed). The step reads vendor_id from
/proc/cpuinfo and installs the matching package. It must run before
configure_grub in boot_ux: grub-mkconfig detects /boot/<vendor>-ucode.img for
its initrd lines, and mkinitcpio's microcode hook embeds it, so the package
has to exist before either generates.

Method: sed-extract install_cpu_microcode from the real `archsetup`, point it
at a fixture cpuinfo, fake pacman_install / display / error_warn.

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

import os
import re
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")

INTEL = "vendor_id\t: GenuineIntel\n"
AMD = "vendor_id\t: AuthenticAMD\n"


def run(cpuinfo_body, missing=False):
    with tempfile.TemporaryDirectory() as d:
        cpuinfo = os.path.join(d, "cpuinfo")
        if not missing:
            with open(cpuinfo, "w") as f:
                f.write(cpuinfo_body)
        script = textwrap.dedent(f"""\
            logfile=/dev/null
            action=""
            display() {{ :; }}
            pacman_install() {{ echo "INSTALL: $1"; }}
            error_warn() {{ echo "WARN: $1"; return 1; }}
            source <(sed -n '/^install_cpu_microcode() {{/,/^}}/p' "{ARCHSETUP}")
            install_cpu_microcode "{cpuinfo}"
            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))


class InstallCpuMicrocode(unittest.TestCase):
    # ------------------------------------------------------------ normal ----
    def test_intel_vendor_installs_intel_ucode(self):
        r = run(f"processor\t: 0\n{INTEL}model name\t: whatever\n")
        self.assertIn("INSTALL: intel-ucode", r.stdout)
        self.assertNotIn("amd-ucode", r.stdout)
        self.assertEqual(rc_of(r), 0)

    def test_amd_vendor_installs_amd_ucode(self):
        r = run(f"processor\t: 0\n{AMD}")
        self.assertIn("INSTALL: amd-ucode", r.stdout)
        self.assertNotIn("intel-ucode", r.stdout)
        self.assertEqual(rc_of(r), 0)

    # ---------------------------------------------------------- boundary ----
    def test_multicore_cpuinfo_installs_exactly_once(self):
        # /proc/cpuinfo repeats vendor_id per logical CPU.
        body = "".join(f"processor\t: {i}\n{AMD}\n" for i in range(16))
        r = run(body)
        self.assertEqual(r.stdout.count("INSTALL:"), 1,
                         "one package install, not one per core")

    def test_space_separated_vendor_line_parses(self):
        # Some kernels/arches pad with spaces rather than a tab.
        r = run("vendor_id       : GenuineIntel\n")
        self.assertIn("INSTALL: intel-ucode", r.stdout)

    def test_vendor_id_substring_elsewhere_does_not_confuse(self):
        # A flags line or model name mentioning a vendor string must not win
        # over the real vendor_id line.
        body = "model name\t: AuthenticAMD emulator\n" + INTEL
        r = run(body)
        self.assertIn("INSTALL: intel-ucode", r.stdout)
        self.assertNotIn("amd-ucode", r.stdout)

    # ------------------------------------------------------------- error ----
    def test_unknown_vendor_warns_and_installs_nothing(self):
        r = run("vendor_id\t: CentaurHauls\n")
        self.assertNotIn("INSTALL:", r.stdout)
        self.assertIn("WARN:", r.stdout)
        self.assertEqual(rc_of(r), 1)

    def test_missing_cpuinfo_warns_and_installs_nothing(self):
        r = run("", missing=True)
        self.assertNotIn("INSTALL:", r.stdout)
        self.assertIn("WARN:", r.stdout)
        self.assertEqual(rc_of(r), 1)
        self.assertEqual(r.returncode, 0)


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