aboutsummaryrefslogtreecommitdiff
path: root/scripts/testing/tests/conftest.py
blob: c805de90fccfe6fb209bd966a000404ac1ae6461 (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
111
# SPDX-License-Identifier: GPL-3.0-or-later
"""Pytest + Testinfra config for archsetup post-install validation.

These tests run on the *host* and connect to the freshly-installed VM over SSH
(Testinfra provides the `host` fixture, parametrized from --hosts). This file
adds two things the bespoke shell harness had that Testinfra does not:

  - Failure attribution. Each check is marked with the layer that owns a
    failure (archsetup | base_install | unknown), mirroring validation.sh's
    attribute_issue. Failures are bucketed and written to --attribution-file
    so run-test.sh can route base-install issues to the archzfs inbox as before.
  - Tiering markers (smoke | integration) so `pytest -m smoke` is a fast gate.

The `target_user` fixture supplies the account archsetup created; it reads
ARCHSETUP_TEST_USER (set by run-test.sh from the VM conf) and defaults to the
historical "cjennings".
"""

import os

import pytest


_ATTRIBUTION_BUCKETS = ("archsetup", "base_install", "unknown")
_failures = {bucket: [] for bucket in _ATTRIBUTION_BUCKETS}


def pytest_addoption(parser):
    parser.addoption(
        "--attribution-file",
        action="store",
        default=None,
        help="write the failure attribution report (archsetup/base_install/unknown) here",
    )


def pytest_configure(config):
    config.addinivalue_line(
        "markers",
        "attribution(bucket): layer that owns a failure — archsetup, base_install, or unknown",
    )
    config.addinivalue_line("markers", "smoke: fast subset (user, key packages, dotfiles present)")
    config.addinivalue_line("markers", "integration: full post-install checks")


@pytest.hookimpl(wrapper=True)
def pytest_runtest_makereport(item, call):
    report = yield
    if report.when == "call" and report.failed:
        marker = item.get_closest_marker("attribution")
        bucket = marker.args[0] if (marker and marker.args) else "archsetup"
        if bucket not in _failures:
            bucket = "unknown"
        _failures[bucket].append(item.nodeid)
    return report


def pytest_sessionfinish(session, exitstatus):
    path = session.config.getoption("--attribution-file")
    if not path:
        return
    with open(path, "w") as fh:
        for bucket in _ATTRIBUTION_BUCKETS:
            fh.write("[%s]\n" % bucket)
            for nodeid in _failures[bucket]:
                fh.write("  %s\n" % nodeid)


@pytest.fixture(scope="session")
def target_user():
    """The account archsetup created in the VM under test."""
    return os.environ.get("ARCHSETUP_TEST_USER", "cjennings")


@pytest.fixture(scope="session")
def home(target_user):
    return "/home/%s" % target_user


@pytest.fixture(scope="session")
def zfs_root(host):
    """True when the VM's root filesystem is ZFS (gates ZFS-specific checks)."""
    return host.run("findmnt -n -o FSTYPE /").stdout.strip() == "zfs"


@pytest.fixture(scope="session")
def has_nvme(host):
    """True when the VM exposes an NVMe device."""
    return host.run("ls /dev/nvme0n1 2>/dev/null").rc == 0


@pytest.fixture(scope="session")
def hyprland_installed(host):
    return host.package("hyprland").is_installed


@pytest.fixture(scope="session")
def dwm_installed(host):
    return host.file("/usr/local/bin/dwm").exists


@pytest.fixture(scope="session")
def compositor_running(host):
    """A graphical session is live (gates socket/portal checks that need one)."""
    return host.run("pgrep -x Hyprland").rc == 0


@pytest.fixture(scope="session")
def on_slirp(host):
    """QEMU user-mode networking (10.0.2.x) — no multicast, so mDNS can't work."""
    return "10.0.2." in host.run("ip -4 addr show").stdout