mirror of
https://github.com/systemd/systemd
synced 2024-10-02 22:37:25 +00:00
test: rework how udev-test is invoked
As part of the build, we would populate build/test/sys/ using sys-script.py, and then udev-test.p[ly] would create a tmpfs instance on build/test/tmpfs and copy the sys tree to build/test/tmpfs/sys. Also, we had udev-test.p[ly] which called test-udev. test-udev was marked as a manual test and installed, but neither udev-test.p[ly] or sys-script.py were. test-udev is renamed to udev-rule-runner, which reduces confusion and frees up the test-udev name. udev-test.py is renamed to test-udev.py. All three files are now installed. test-udev.py is modified to internally call sys-script.py to set up the sys tree. Copying and creating it from scratch should take the same amount of time. We avoid having a magic directory, everything is now done underneath a temporary directory. test-udev.py is now a normal installed test, and run-unit-tests.py will pick it up. When test-udev.py is invoked from meson, the path to udev-rule-runner is passed via envvar; when it is invoked via run-unit-tests.py or directly, it looks for udev-rule-runner in a relative path. The goal of this whole change is to let Debian drop the 'udev' test. It called sys-script.py and udev-test.pl from the source directory and had to recreate a bunch of the logic. Now test-udev.py will now be called via 'upstream'.
This commit is contained in:
parent
09ea351b6f
commit
0454cf05d3
13
meson.build
13
meson.build
|
@ -4415,6 +4415,7 @@ foreach test : simple_tests
|
|||
tests += { 'sources' : [test] }
|
||||
endforeach
|
||||
|
||||
TESTS = {}
|
||||
foreach test : tests
|
||||
sources = test.get('sources')
|
||||
condition = test.get('condition', '')
|
||||
|
@ -4466,6 +4467,8 @@ foreach test : tests
|
|||
suite : suite,
|
||||
is_parallel : test.get('parallel', true))
|
||||
endif
|
||||
|
||||
TESTS += { name : exe }
|
||||
endforeach
|
||||
|
||||
exe = executable(
|
||||
|
@ -4527,6 +4530,16 @@ if want_tests != 'false' and static_libudev_pic
|
|||
test('test-libudev-static-sym', exe)
|
||||
endif
|
||||
|
||||
if want_tests != 'false'
|
||||
udev_rule_runner = TESTS['udev-rule-runner'].full_path()
|
||||
|
||||
test('test-udev',
|
||||
test_udev_py,
|
||||
args : ['-v'],
|
||||
env : ['UDEV_RULE_RUNNER=@0@'.format(udev_rule_runner)],
|
||||
timeout : 180)
|
||||
endif
|
||||
|
||||
############################################################
|
||||
|
||||
foreach fuzzer : simple_fuzzers
|
||||
|
|
|
@ -409,7 +409,7 @@ tests += [
|
|||
'timeout' : 120,
|
||||
},
|
||||
{
|
||||
'sources' : files('test-udev.c'),
|
||||
'sources' : files('udev-rule-runner.c'),
|
||||
'link_with' : [
|
||||
libshared,
|
||||
libudevd_core,
|
||||
|
|
|
@ -62,11 +62,11 @@ static int fake_filesystems(void) {
|
|||
const char *error;
|
||||
bool ignore_mount_error;
|
||||
} fakefss[] = {
|
||||
{ "test/tmpfs/sys", "/sys", "Failed to mount test /sys", false },
|
||||
{ "test/tmpfs/dev", "/dev", "Failed to mount test /dev", false },
|
||||
{ "test/run", "/run", "Failed to mount test /run", false },
|
||||
{ "test/run", "/etc/udev/rules.d", "Failed to mount empty /etc/udev/rules.d", true },
|
||||
{ "test/run", UDEVLIBEXECDIR "/rules.d", "Failed to mount empty " UDEVLIBEXECDIR "/rules.d", true },
|
||||
{ "tmpfs/sys", "/sys", "Failed to mount test /sys", false },
|
||||
{ "tmpfs/dev", "/dev", "Failed to mount test /dev", false },
|
||||
{ "run", "/run", "Failed to mount test /run", false },
|
||||
{ "run", "/etc/udev/rules.d", "Failed to mount empty /etc/udev/rules.d", true },
|
||||
{ "run", UDEVLIBEXECDIR "/rules.d", "Failed to mount empty " UDEVLIBEXECDIR "/rules.d", true },
|
||||
};
|
||||
int r;
|
||||
|
|
@ -123,19 +123,14 @@ endif
|
|||
|
||||
############################################################
|
||||
|
||||
# prepare test/sys tree
|
||||
sys_script_py = find_program('sys-script.py')
|
||||
custom_target(
|
||||
'sys',
|
||||
command : [sys_script_py, meson.current_build_dir()],
|
||||
output : 'sys',
|
||||
build_by_default : want_tests != 'false')
|
||||
sys_script_py = files('sys-script.py')
|
||||
test_udev_py = files('test-udev.py')
|
||||
|
||||
if want_tests != 'false'
|
||||
test('udev-test',
|
||||
files('udev-test.py'),
|
||||
args : ['-v'],
|
||||
timeout : 180)
|
||||
if install_tests
|
||||
install_data(
|
||||
sys_script_py,
|
||||
test_udev_py,
|
||||
install_dir : unittestsdir)
|
||||
endif
|
||||
|
||||
############################################################
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# pylint: disable=missing-docstring,redefined-outer-name,invalid-name
|
||||
# pylint: disable=unspecified-encoding,no-else-return,line-too-long,too-many-lines
|
||||
# pylint: disable=multiple-imports,too-many-instance-attributes,consider-using-with
|
||||
# pylint: disable=global-statement
|
||||
|
||||
# udev test
|
||||
#
|
||||
|
@ -26,9 +27,10 @@ import re
|
|||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Callable, Optional
|
||||
|
||||
try:
|
||||
import pytest
|
||||
|
@ -37,16 +39,15 @@ except ImportError as e:
|
|||
sys.exit(77)
|
||||
|
||||
|
||||
# TODO: pass path to test-udev from outside
|
||||
UDEV_BIN = './test-udev'
|
||||
UDEV_RUN = Path('test/run')
|
||||
UDEV_RULES_DIR = UDEV_RUN / 'udev/rules.d'
|
||||
UDEV_RULES = UDEV_RULES_DIR / 'udev-test.rules'
|
||||
SYS_SCRIPT = Path(__file__).with_name('sys-script.py')
|
||||
try:
|
||||
UDEV_BIN = Path(os.environ['UDEV_RULE_RUNNER'])
|
||||
except KeyError:
|
||||
UDEV_BIN = Path(__file__).parent / 'manual/udev-rule-runner'
|
||||
UDEV_BIN = UDEV_BIN.absolute()
|
||||
|
||||
UDEV_RUN = Path('test/run')
|
||||
UDEV_TMPFS = Path('test/tmpfs')
|
||||
UDEV_DEV = UDEV_TMPFS / 'dev'
|
||||
UDEV_SYS = UDEV_TMPFS / 'sys'
|
||||
# Those will be set by the udev_setup() fixture
|
||||
UDEV_RUN = UDEV_RULES = UDEV_DEV = UDEV_SYS = None
|
||||
|
||||
# Relax sd-device's sysfs verification, since we want to provide a fake sysfs
|
||||
# here that actually is a tmpfs.
|
||||
|
@ -178,14 +179,23 @@ class Rules:
|
|||
desc: str
|
||||
devices: list[Device]
|
||||
rules: str
|
||||
device_generator: Callable = None
|
||||
repeat: int = 1
|
||||
delay: Optional[int] = None
|
||||
|
||||
@classmethod
|
||||
def new(cls, desc: str, *devices, rules = None, **kwargs):
|
||||
def new(cls, desc: str, *devices, rules=None, device_generator=None, **kwargs):
|
||||
assert rules.startswith('\n')
|
||||
rules = textwrap.dedent(rules[1:]) if rules else ''
|
||||
return cls(desc, devices, rules, **kwargs)
|
||||
|
||||
assert bool(devices) ^ bool(device_generator)
|
||||
|
||||
return cls(desc, devices, rules, device_generator=device_generator, **kwargs)
|
||||
|
||||
def generate_devices(self) -> None:
|
||||
# We can't do this when the class is created, because setup is done later.
|
||||
if self.device_generator:
|
||||
self.devices = self.device_generator()
|
||||
|
||||
def create_rules_file(self) -> None:
|
||||
# create temporary rules
|
||||
|
@ -2298,7 +2308,8 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c \"printf %%s 'foo1 foo2' | grep 'foo1 f
|
|||
|
||||
Rules.new(
|
||||
'all_block_devs',
|
||||
*all_block_devs(lambda name: (["blockdev"], None) if name.endswith('/sda6') else (None, None)),
|
||||
device_generator = lambda: \
|
||||
all_block_devs(lambda name: (["blockdev"], None) if name.endswith('/sda6') else (None, None)),
|
||||
repeat = 10,
|
||||
rules = r"""
|
||||
SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sd*", SYMLINK+="blockdev"
|
||||
|
@ -2344,15 +2355,27 @@ def udev_setup():
|
|||
if issue:
|
||||
pytest.skip(issue)
|
||||
|
||||
subprocess.run(['umount', UDEV_TMPFS],
|
||||
global UDEV_RUN, UDEV_RULES, UDEV_DEV, UDEV_SYS
|
||||
|
||||
_tmpdir = tempfile.TemporaryDirectory()
|
||||
tmpdir = Path(_tmpdir.name)
|
||||
|
||||
UDEV_RUN = tmpdir / 'run'
|
||||
UDEV_RULES = UDEV_RUN / 'udev-test.rules'
|
||||
|
||||
udev_tmpfs = tmpdir / 'tmpfs'
|
||||
UDEV_DEV = udev_tmpfs / 'dev'
|
||||
UDEV_SYS = udev_tmpfs / 'sys'
|
||||
|
||||
subprocess.run(['umount', udev_tmpfs],
|
||||
stderr=subprocess.DEVNULL,
|
||||
check=False)
|
||||
UDEV_TMPFS.mkdir(exist_ok=True, parents=True)
|
||||
udev_tmpfs.mkdir(exist_ok=True, parents=True)
|
||||
|
||||
subprocess.check_call(['mount', '-v',
|
||||
'-t', 'tmpfs',
|
||||
'-o', 'rw,mode=0755,nosuid,noexec',
|
||||
'tmpfs', UDEV_TMPFS])
|
||||
'tmpfs', udev_tmpfs])
|
||||
|
||||
UDEV_DEV.mkdir(exist_ok=True)
|
||||
# setting group and mode of udev_dev ensures the tests work
|
||||
|
@ -2367,10 +2390,12 @@ def udev_setup():
|
|||
os.mknod(sda, 0o600 | stat.S_IFBLK, os.makedev(8, 0))
|
||||
sda.unlink()
|
||||
|
||||
subprocess.check_call(['cp', '-r', 'test/sys/', UDEV_SYS])
|
||||
subprocess.check_call([SYS_SCRIPT, UDEV_SYS.parent])
|
||||
subprocess.check_call(['rm', '-rf', UDEV_RUN])
|
||||
UDEV_RUN.mkdir(parents=True)
|
||||
|
||||
os.chdir(tmpdir)
|
||||
|
||||
if subprocess.run([UDEV_BIN, 'check'],
|
||||
check=False).returncode != 0:
|
||||
pytest.skip(f'{UDEV_BIN} failed to set up the environment, skipping the test',
|
||||
|
@ -2379,8 +2404,8 @@ def udev_setup():
|
|||
yield
|
||||
|
||||
subprocess.check_call(['rm', '-rf', UDEV_RUN])
|
||||
subprocess.check_call(['umount', '-v', UDEV_TMPFS])
|
||||
UDEV_TMPFS.rmdir()
|
||||
subprocess.check_call(['umount', '-v', udev_tmpfs])
|
||||
udev_tmpfs.rmdir()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("rules", RULES, ids=(rule.desc for rule in RULES))
|
||||
|
@ -2388,6 +2413,7 @@ def test_udev(rules: Rules, udev_setup):
|
|||
assert udev_setup is None
|
||||
|
||||
rules.create_rules_file()
|
||||
rules.generate_devices()
|
||||
|
||||
for _ in range(rules.repeat):
|
||||
fork_and_run_udev('add', rules)
|
Loading…
Reference in a new issue