deb-mock/behave/steps/other.py
robojerk 4c0dcb2522
Some checks failed
Build Deb-Mock Package / build (push) Successful in 54s
Lint Code / Lint All Code (push) Failing after 1s
Test Deb-Mock Build / test (push) Failing after 36s
enhance: Add comprehensive .gitignore for deb-mock project
- Add mock-specific build artifacts (chroot/, mock-*, mockroot/)
- Include package build files (*.deb, *.changes, *.buildinfo)
- Add development tools (.coverage, .pytest_cache, .tox)
- Include system files (.DS_Store, Thumbs.db, ._*)
- Add temporary and backup files (*.tmp, *.bak, *.backup)
- Include local configuration overrides (config.local.yaml, .env.local)
- Add test artifacts and documentation builds
- Comprehensive coverage for Python build system project

This ensures build artifacts, chroot environments, and development
tools are properly ignored in version control.
2025-08-18 23:37:49 -07:00

333 lines
9.8 KiB
Python

""" Generic testing steps """
import glob
import importlib
import json
import os
import shutil
import tarfile
import tempfile
from pathlib import Path
from hamcrest import (
assert_that,
contains_string,
ends_with,
equal_to,
has_item,
has_entries,
has_length,
not_,
)
import jsonschema
from behave import given, when, then # pylint: disable=no-name-in-module
from testlib.commands import run, no_output
# flake8: noqa
# pylint: disable=missing-function-docstring,function-redefined
# mypy: disable-error-code="no-redef"
def _first_int(string, max_lines=20):
for line in string.split("\n")[:max_lines]:
if not line:
continue
first_word = line.split()[0]
if first_word.isdigit():
return first_word
raise RuntimeError("unexpected dnf history output")
def add_cleanup_last_transaction(context):
# DNF5 support https://github.com/rpm-software-management/dnf5/issues/140
dnf = ["sudo", "/usr/bin/dnf", "history"]
_, out, _ = run(dnf + ["list"])
transaction_id = _first_int(out)
def _revert_transaction(_context):
cmd = dnf + ["undo", transaction_id, "-y"]
with no_output():
assert_that(run(cmd)[0], equal_to(0))
context.add_cleanup(_revert_transaction, context)
@given('an unique mock namespace')
def step_impl(context):
print(f"using uniqueext {context.uniqueext}")
context.uniqueext_used = True
@given('the {package} package {state} installed on host')
def step_impl(context, package, state):
"""
Install the package, and uninstall in post- action. If state is "not", then
just check it is not installed.
"""
is_installed, _, _ = run(["rpm", "-q", package])
# exit_status 0 => installed
is_installed = bool(not is_installed)
if "not" in state:
if not is_installed:
return # nothing to do
# Remove the package and schedule its removal
cmd = ["sudo", "dnf", "-y", "remove", package]
assert_that(run(cmd)[0], equal_to(0))
# schedule removal
add_cleanup_last_transaction(context)
return
if is_installed:
return
# install the package, and schedule removal
def _uninstall_pkg(_context):
cmd = ["sudo", "dnf", "-y", "remove", package]
with no_output():
assert_that(run(cmd)[0], equal_to(0))
cmd = ["sudo", "dnf", "-y", "install", package]
assert_that(run(cmd)[0], equal_to(0))
context.add_cleanup(_uninstall_pkg, context)
@given('pre-intitialized chroot')
def step_impl(context):
context.mock.init()
@given('a custom third-party repository is used for builds')
def step_impl(context):
context.add_repos.append(
"https://raw.githubusercontent.com/rpm-software-management/"
"mock-test-data/main/repo/"
)
@given("a created local repository")
def step_impl(context):
context.local_repo = tempfile.mkdtemp(prefix="mock-tests-local-repo-")
run(["createrepo_c", context.local_repo])
@given('the local repo contains a "{rpm}" RPM')
def step_impl(context, rpm):
rpm = context.download_rpm(rpm)
shutil.move(rpm, context.local_repo)
run(["createrepo_c", context.local_repo])
@given("the local repo is used for builds")
def step_impl(context):
context.add_repos.append(context.local_repo)
@when('a build is depending on third-party repo requested')
@when('a build which requires the "always-installable" RPM is requested')
def step_impl(context):
local_file = context.download_rpm("buildrequires-always-installable")
context.mock.rebuild([local_file])
@then('the build succeeds')
def step_impl(context):
assert os.path.exists(context.mock.resultdir)
rpms = glob.glob(os.path.join(context.mock.resultdir, "*.rpm"))
print("Found RPMs: " + ", ".join(rpms))
assert_that(rpms, has_item(ends_with(".src.rpm")))
assert_that(rpms, has_item(not_(ends_with(".src.rpm"))))
@when('mock is run with "{options}" options')
def step_impl(context, options):
options = options.split()
context.last_cmd = run(['mock'] + options)
@given('mock is always executed with "{options}"')
def step_impl(context, options):
options = options.split()
context.mock.common_opts += options
@then('the exit code is {code}')
def step_impl(context, code):
code = int(code)
assert_that(context.last_cmd[0], equal_to(code))
@then('the one-liner error contains "{expected_message}"')
def step_impl(context, expected_message):
err = context.last_cmd[2].splitlines()
assert_that(err, has_length(1))
assert_that(err[0], contains_string(expected_message))
def _rebuild_online(context, chroot=None, package=None):
package = package or "mock-test-bump-version-1-0.src.rpm"
url = context.test_storage + package
if chroot:
context.mock.chroot = chroot
context.mock.chroot_opt = chroot
context.mock.rebuild([url])
@when('an online source RPM is rebuilt')
def step_impl(context):
_rebuild_online(context)
@when('an online source RPM is rebuilt against {chroot}')
def step_impl(context, chroot):
_rebuild_online(context, chroot)
@when('an online SRPM {package} is rebuilt against {chroot}')
def step_impl(context, package, chroot):
_rebuild_online(context, chroot, package)
@then('{output} contains "{text}"')
def step_impl(context, output, text):
index = 1 if output == "stdout" else 2
real_output = context.last_cmd[index]
assert_that(real_output, contains_string(text))
@when('{call} method from {module} is called with {args} args')
def step_impl(context, call, module, args):
imp = importlib.import_module(module)
method = getattr(imp, call)
args = args.split()
context.last_method_call_retval = method(*args)
@then('the return value contains a field "{field}={value}"')
def step_impl(context, field, value):
assert_that(context.last_method_call_retval[field],
equal_to(value))
@when('deps for {srpm} are calculated against {chroot}')
def step_impl(context, srpm, chroot):
url = context.test_storage + srpm
context.mock.calculate_deps(url, chroot)
@when('a local repository is created from lockfile')
def step_impl(context):
mock_run = context.mock_runs["calculate-build-deps"][-1]
lockfile = mock_run["lockfile"]
context.local_repo = tempfile.mkdtemp(prefix="mock-tests-local-repo-")
cmd = ["mock-hermetic-repo", "--lockfile", lockfile, "--output-repo",
context.local_repo]
assert_that(run(cmd)[0], equal_to(0))
@when('a hermetic build is retriggered with the lockfile and repository')
def step_impl(context):
context.mock.hermetic_build()
@then('the produced lockfile is validated properly')
def step_impl(context):
mock_run = context.mock_runs["calculate-build-deps"][-1]
lockfile = mock_run["lockfile"]
with open(lockfile, "r", encoding="utf-8") as fd:
lockfile_data = json.load(fd)
assert_that(lockfile_data["buildroot"]["rpms"],
has_item(has_entries({"name": "filesystem"})))
schemafile = os.path.join(os.path.dirname(__file__), '..', '..',
"mock", "docs",
"buildroot-lock-schema-1.1.0.json")
with open(schemafile, "r", encoding="utf-8") as fd:
schema = json.load(fd)
jsonschema.validate(lockfile_data, schema)
@given('next mock call uses {option} option')
def step_impl(context, option):
context.next_mock_options.append(option)
@then("the directory {directory} is empty")
def step_impl(_, directory):
assert_that(os.path.exists(directory), equal_to(True))
assert_that(not os.listdir(directory), equal_to(True))
@given('chroot_scan is enabled for {regex}')
def step_impl(context, regex):
context.custom_config += f"""\
config_opts['plugin_conf']['chroot_scan_enable'] = True
config_opts['plugin_conf']['chroot_scan_opts']['regexes'] = ["{regex}"]
config_opts['plugin_conf']['chroot_scan_opts']['only_failed'] = False
"""
@then('{file} file is in chroot_scan result dir')
def step_impl(context, file):
resultdir = os.path.join(context.mock.resultdir, 'chroot_scan')
# Find the expected file
found = False
print("resultdir: ", resultdir)
for _, _, files in os.walk(resultdir):
for f in files:
print(f)
if f == file:
found = True
break
if found:
break
assert_that(found, equal_to(True))
@given('chroot_scan is configured to produce tarball')
def step_impl(context):
context.custom_config += """\
config_opts['plugin_conf']['chroot_scan_opts']['write_tar'] = True
"""
@then('ownership of all chroot_scan files is correct')
def step_impl(context):
resultdir = os.path.join(context.mock.resultdir, 'chroot_scan')
for root, dirs, files in os.walk(resultdir):
for f in files + dirs:
path = Path(root) / f
assert_that(path.group(), equal_to("mock"))
assert_that(path.owner(), equal_to(context.current_user))
@then('chroot_scan tarball has correct perms and provides dnf5.log')
def step_impl(context):
tarball = Path(context.mock.resultdir, 'chroot_scan.tar.gz')
with tarfile.open(tarball, 'r:gz') as tarf:
for file in tarf.getnames():
if file.endswith("dnf5.log"):
break
assert_that(tarball.group(), equal_to("mock"))
assert_that(tarball.owner(), equal_to(context.current_user))
@when('OCI tarball from {chroot} backed up and will be used')
def step_impl(context, chroot):
resultdir = f"/var/lib/mock/{chroot}-{context.uniqueext}/result"
tarball_base = "buildroot-oci.tar"
tarball = os.path.join(resultdir, tarball_base)
assert os.path.exists(tarball)
shutil.copy(tarball, context.workdir)
context.mock.buildroot_image = os.path.join(context.workdir, tarball_base)
@when('the {chroot} chroot is scrubbed')
def step_impl(context, chroot):
context.mock.scrub(chroot)