mirror of
https://github.com/SCST-project/scst.git
synced 2026-10-22 14:12:33 +00:00
Provide kunit-build for all test-owning modules and their dependencies, kunit-run for existing artifacts, and kunit-test for a sequential build and run even under make -j. Allow root builds and run every command with the caller's privileges. Require root for module execution. Map KUNIT_TEST and KUNIT_LOG to private Kbuild switches. Default KUNIT_LABEL to ALL and use KUNIT_ARGS for other runner options and module paths. Resolve artifacts from explicit arguments, configuration or the recorded build, preserving spaces. Group and align the Makefile help for component tests, KUnit and ordinary build targets. Read label-to-suite mappings and dependency order from module metadata, without a separate Python owner registry. Reject dependency cycles and missing SCST core artifacts. Load external kernel dependencies with modprobe before preparing KUnit; keep shared system modules loaded. Load only selected SCST owners and their dependencies from the supplied build and trigger suites through native KUnit debugfs. Support built-in and modular KUnit with autorun disabled. Load modular KUnit with known parameters when absent and release it after SCST cleanup or a clean initial load failure. Reuse an existing instance only after validating its effective parameters in sysfs; reject invalid or unreadable values without changing the module. Never unload preloaded KUnit. Check global filters before loading SCST. Capture fresh kernel records and parse suites with the upstream KTAP parser. Preserve logs and skip reasons, associating first-parameter diagnostics with their case without changing plans or results. Stop module operations on incomplete evidence, diagnostics or retained fixtures. Continue after ordinary assertion failures. Finish capturing output after command exit. Distinguish suites not started from missing results, and count each Oops as one event. Print concise errors and colored terminal statuses as suites finish. Group failure details after progress. Normalize each assertion block independently of printk messages while retaining value indentation. Follow with explicit stop reasons and a compact SCST build summary. Reserve module headings, author, license, source checksums, vermagic and other modules for verbose output. Keep full case logs, module metadata and kernel diagnostics in plain-text and JSON reports. Cover build ordering, selection, kernel setup, dependency preparation, runner lifecycle and reporting with mocked operations. Document VM setup, build, execution and extension in the KUnit guide. Add kunit-clean for test-owning component builds. Invalidate the recorded module list and clear tooling caches on ordinary and component clean paths too, including failed cleanup. Preserve reports and user settings. Check all tooling, kunit-build and cleanup in one CI job. Pin the Fedora container by digest and fetch a fixed kernel-devel package from Koji with a SHA-256 check, independently of the regression matrix. Save build evidence without building a kernel or loading modules.
118 lines
6.3 KiB
Python
118 lines
6.3 KiB
Python
# SPDX-License-Identifier: GPL-2.0-only
|
|
"""Exercise cleanup recipes in temporary trees without touching real modules."""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
COMPONENTS = next(line.split(':=', 1)[1].split()
|
|
for line in (ROOT / 'scst/src/kunit/modules.mk').read_text().splitlines()
|
|
if line.startswith('SCST_KUNIT_COMPONENTS := '))
|
|
|
|
|
|
class CleanTests(unittest.TestCase):
|
|
def check_clean(self, directory, goal, fail=False, tooling=True):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = Path(tmp)
|
|
for path in ('Makefile', 'scst/Makefile', 'scst/src/Makefile',
|
|
'scst/src/dev_handlers/Makefile', 'scst/src/kunit/select.mk',
|
|
'scst/src/kunit/modules.mk', 'scst/include/scst_const.h',
|
|
'iscsi-scst/Makefile', 'iscsi-scst/usr/Makefile'):
|
|
target = root / path
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copyfile(ROOT / path, target)
|
|
# Other components are deliberately inert; kunit-clean must not visit them.
|
|
for component in ('qla2x00t-32gbit/qla2x00-target', 'srpt', 'scst_local', 'fcst', 'usr', 'scstadmin'):
|
|
target = root / component
|
|
target.mkdir(parents=True)
|
|
(target / 'Makefile').write_text('clean extraclean:;@rm -f visited.o\n')
|
|
(target / 'visited.o').touch()
|
|
kernel = root / 'kernel'
|
|
kernel.mkdir()
|
|
(kernel / 'Makefile').write_text(
|
|
'clean:\n'
|
|
'\t@test "$(FAIL_CLEAN)" != y\n'
|
|
'\t@find "$(M)" -type f \\( -name "*.o" -o -name "*.ko" \\) -delete\n')
|
|
artifacts = ['scst/src/scst.ko', 'scst/src/kunit/lib/suites/test.o',
|
|
'scst/src/dev_handlers/scst_vdisk.ko',
|
|
'scst/src/dev_handlers/kunit/vdisk/suites/test.o',
|
|
'iscsi-scst/kernel/iscsi-scst.ko',
|
|
'iscsi-scst/kernel/kunit/suites/test.o']
|
|
metadata = ['.kunit-modules', '.kunit-modules.tmp']
|
|
if tooling:
|
|
metadata += ['scripts/kunit/__pycache__/run.pyc',
|
|
'scripts/kunit/upstream/__pycache__/kunit_parser.pyc']
|
|
generated = ['scst/include/build_mode.h', 'scst/include/build_mode.h.tmp',
|
|
'scst/include/scst_itf_ver.h',
|
|
'iscsi-scst/include/iscsi_scst_itf_ver.h',
|
|
'iscsi-scst/conftest/sock_recvmsg/result-test.txt']
|
|
preserved = ['.kunit-run.toml', 'kunit-results/run/report.txt',
|
|
'scst/build_mode', 'scst/src/kunit/lib/suites/test.c']
|
|
for path in artifacts + metadata + generated + preserved:
|
|
target = root / path
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
target.write_text('preserve me\n' if path in preserved else '')
|
|
env = {key: value for key, value in os.environ.items()
|
|
if key not in ('MAKEFLAGS', 'MFLAGS', 'KDIR', 'KVER')
|
|
and not key.startswith(('KUNIT_', 'CONFIG_SCST_KUNIT_'))}
|
|
result = subprocess.run(['make', '-s', '-j4', '-C', str(root / directory), goal,
|
|
'KDIR=' + str(kernel), 'KVER=test',
|
|
'FAIL_CLEAN=' + ('y' if fail else 'n'),
|
|
'OFED_FLAVOR=in-tree',
|
|
'OFED_KERNEL_IB_DEVEL_RPM_INSTALLED=false',
|
|
'OFED_COMPAT_RDMA_DEVEL_RPM_INSTALLED=false'],
|
|
env=env, capture_output=True, text=True)
|
|
self.assertEqual(result.returncode == 0, not fail, result.stdout + result.stderr)
|
|
for path in metadata:
|
|
self.assertFalse((root / path).exists(), path)
|
|
if not tooling:
|
|
self.assertFalse((root / 'scripts').exists())
|
|
for path in preserved:
|
|
self.assertEqual((root / path).read_text(), 'preserve me\n', path)
|
|
if not fail and directory == '' and goal in ('clean', 'extraclean', 'kunit-clean'):
|
|
for path in artifacts + generated:
|
|
if goal == 'kunit-clean' and path.split('/')[0] not in COMPONENTS:
|
|
continue
|
|
self.assertFalse((root / path).exists(), path)
|
|
if goal == 'kunit-clean':
|
|
self.assertTrue((root / 'qla2x00t-32gbit/qla2x00-target/visited.o').exists())
|
|
|
|
def test_top_level_cleanup_removes_build_state_and_preserves_reports(self):
|
|
for goal in ('kunit-clean', 'clean', 'extraclean'):
|
|
with self.subTest(goal=goal):
|
|
self.check_clean('', goal)
|
|
|
|
def test_component_cleanup_invalidates_module_list(self):
|
|
for directory in ('scst', 'scst/src', 'scst/src/dev_handlers', 'iscsi-scst'):
|
|
if directory != 'scst' and 'kunit/select.mk' not in (ROOT / directory / 'Makefile').read_text():
|
|
continue
|
|
for goal in ('clean', 'extraclean'):
|
|
with self.subTest(directory=directory, goal=goal):
|
|
self.check_clean(directory, goal)
|
|
for goal in ('scst_clean', 'scst_extraclean', 'iscsi_clean', 'iscsi_extraclean'):
|
|
if goal.startswith('iscsi_') and 'iscsi-scst' not in COMPONENTS:
|
|
continue
|
|
with self.subTest(goal=goal):
|
|
self.check_clean('', goal)
|
|
|
|
def test_failed_cleanup_still_invalidates_module_list(self):
|
|
self.check_clean('', 'kunit-clean', fail=True)
|
|
|
|
def test_cleanup_without_packaged_tooling(self):
|
|
# Debian DKMS ships module sources without the scripts/ directory.
|
|
for directory in ('', 'scst', 'scst/src', 'iscsi-scst'):
|
|
if directory and 'kunit/select.mk' not in (ROOT / directory / 'Makefile').read_text():
|
|
continue
|
|
for goal in ('clean', 'extraclean'):
|
|
with self.subTest(directory=directory, goal=goal):
|
|
self.check_clean(directory, goal, tooling=False)
|
|
self.check_clean('', 'kunit-clean', tooling=False)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|