test: add pytest and test config load/migrate (MR 2252)

re-introduce pytest, add a conftest.py with some useful fixtures and
basic tests for config loading.

This just checks that we can load the config and migrate it from the old
2.3.x format to the new 3.0 format with the new mirrors section.

Testing anything that requires args or Context should probably wait
until we can properly model state (since global state like in
get_context() really doesn't jive with pytest).

Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
This commit is contained in:
Caleb Connolly 2024-06-10 23:15:19 +02:00 committed by Oliver Smith
parent 0db01a2919
commit ff86792fb6
No known key found for this signature in database
GPG key ID: 5AE7F5513E0885CB
4 changed files with 207 additions and 0 deletions

3
.gitignore vendored
View file

@ -9,6 +9,9 @@
*.rej *.rej
*.orig *.orig
# Pytest tmp dir
.pytest_tmp
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]

View file

@ -0,0 +1,94 @@
from pathlib import Path
import pytest
import pmb.config
from pmb.core.config import SystemdConfig
"""Test the config file serialization and deserialization."""
def test_load(config_file):
config = pmb.config.load(config_file)
assert config.build_default_device_arch
assert config.ccache_size == "5G"
assert config.device == "qemu-amd64"
assert config.extra_packages == "neofetch,neovim,reboot-mode"
assert config.hostname == "qemu-amd64"
assert not config.is_default_channel
assert config.jobs == "8"
assert config.kernel == "edge"
assert config.locale == "C.UTF-8"
assert config.ssh_keys
assert config.sudo_timer
assert config.systemd == SystemdConfig.ALWAYS
assert config.timezone == "Europe/Berlin"
assert config.ui == "gnome"
assert config.providers == {}
assert config.mirrors["pmaports"] is not None
assert ".pytest_tmp" in config.work.parts
@pytest.fixture
def config_file_2_3_x(tmp_path: Path):
"""Fixture to create a temporary pmbootstrap.cfg file with 2.3.x format."""
file = tmp_path / "pmbootstrap.cfg"
contents = """[pmbootstrap]
aports = /home/user/.local/var/pmbootstrap/cache_git/pmaports
ccache_size = 32G
is_default_channel = False
device = oneplus-fajita
extra_packages = none
hostname = pmos
build_pkgs_on_install = True
jobs = 32
kernel = edge
keymap =
locale = C.UTF-8
nonfree_firmware = True
nonfree_userland = False
ssh_keys = True
timezone = Europe/London
ui = gnome-mobile
ui_extras = False
user = user
work = /home/user/.local/var/pmbootstrap
boot_size = 256
extra_space = 0
sudo_timer = True
mirrors_postmarketos = http://mirror.postmarketos.org/postmarketos/
mirror_alpine = http://dl-cdn.alpinelinux.org/alpine/
ssh_key_glob = ~/.ssh/id_*.pub
qemu_redir_stdio = True
build_default_device_arch = True
merge_usr = True
auto_checksum = True
systemd = always
[providers]
"""
open(file, "w").write(contents)
return file
def test_migrate_2_to_3(config_file_2_3_x, tmp_path, monkeypatch):
tmp_path = tmp_path / "pmbootstrap-new.cfg"
did_migrate = False
def mock_save(path, config):
nonlocal did_migrate
did_migrate = True
monkeypatch.setattr(pmb.config.file, "save", mock_save)
config = pmb.config.load(config_file_2_3_x)
# The 2.3.x to 3.0 migration removes these keys from the
# config in favour of a new [mirrors] section.
# It should be automatically migrated.
assert not hasattr(config, "mirror_alpine")
assert not hasattr(config, "mirrors_postmarketos")
# Check that save was called (which happens on a config migration)
assert did_migrate

103
pmb/conftest.py Normal file
View file

@ -0,0 +1,103 @@
import os
from pathlib import Path
import pytest
from contextlib import contextmanager
@contextmanager
def _fixture_context(val):
yield val
@pytest.fixture(scope="session")
def config_file_session(tmp_path_factory):
"""Fixture to create a temporary pmbootstrap.cfg file."""
tmp_path = tmp_path_factory.mktemp("pmbootstrap")
file = tmp_path / "pmbootstrap.cfg"
workdir = tmp_path / "work"
workdir.mkdir()
contents = """[pmbootstrap]
build_default_device_arch = True
ccache_size = 5G
device = qemu-amd64
extra_packages = neofetch,neovim,reboot-mode
hostname = qemu-amd64
is_default_channel = False
jobs = 8
kernel = edge
locale = C.UTF-8
ssh_keys = True
sudo_timer = True
systemd = always
timezone = Europe/Berlin
ui = gnome
work = {0}
[providers]
[mirrors]
""".format(workdir)
open(file, "w").write(contents)
return file
@pytest.fixture
def config_file(config_file_session):
"""Fixture to create a temporary pmbootstrap.cfg file."""
with _fixture_context(config_file_session) as val:
yield val
@pytest.fixture(autouse=True)
def setup_logging(tmp_path: Path):
"""Setup logging for all tests."""
import logging
logfile = tmp_path / "test.log"
logging.basicConfig(level=logging.DEBUG, force=True, filename=logfile)
@pytest.fixture(autouse=True)
def setup_mock_ask(monkeypatch):
"""Common setup to mock cli.ask() to avoid reading from stdin"""
import pmb.helpers.cli
def mock_ask(question="Continue?", choices=["y", "n"], default="n",
lowercase_answer=True, validation_regex=None, complete=None):
return default
monkeypatch.setattr(pmb.helpers.cli, "ask", mock_ask)
# FIXME: get_context() at runtime somehow doesn't return the
# custom context we set up here.
# @pytest.fixture(scope="session")
# def pmb_args(config_file_session):
# """This is (still) a hack, since a bunch of the codebase still
# expects some global state to be initialised. We do that here."""
# from pmb.types import PmbArgs
# from pmb.helpers.args import init as init_args
# args = PmbArgs()
# args.config = config_file_session
# args.aports = None
# args.timeout = 900
# args.details_to_stdout = False
# args.quiet = False
# args.verbose = False
# args.offline = False
# args.action = "init"
# args.cross = False
# args.log = Path()
# print("init_args")
# return init_args(args)
@pytest.fixture
def foreign_arch():
"""Fixture to return the foreign arch."""
from pmb.core.arch import Arch
if os.uname().machine == "x86_64":
return Arch.aarch64
return Arch.x86_64

View file

@ -33,3 +33,10 @@ exclude = ["aports", "docs", "keys", "test", "test.pmb_test"]
# E722: do not use bare except # E722: do not use bare except
lint.ignore=["E402", "E722"] lint.ignore=["E402", "E722"]
line-length=100 line-length=100
[tool.pytest.ini_options]
addopts = [
"--import-mode=importlib",
"--basetemp=.pytest_tmp"
]