]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: rework how udev-test is invoked
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 5 May 2023 12:46:34 +0000 (14:46 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 9 May 2023 06:11:10 +0000 (08:11 +0200)
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'.

meson.build
src/test/meson.build
src/test/udev-rule-runner.c [moved from src/test/test-udev.c with 89% similarity]
test/meson.build
test/test-udev.py [moved from test/udev-test.py with 98% similarity]

index 224e6251d2f1a6283a3382cfdb685cfdffd2f4aa..f6c7279fd526a78e63165294bd95c5c5aa00c846 100644 (file)
@@ -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
index 8a5e47f004a37de3ef42aef5810ba9337b611343..8e76df624d53f330e8e9853ac6f70236ce6a1666 100644 (file)
@@ -409,7 +409,7 @@ tests += [
                 'timeout' : 120,
         },
         {
-                'sources' : files('test-udev.c'),
+                'sources' : files('udev-rule-runner.c'),
                 'link_with' : [
                         libshared,
                         libudevd_core,
similarity index 89%
rename from src/test/test-udev.c
rename to src/test/udev-rule-runner.c
index 00ca79d0eba7bb96ac7f72732f4db542be15e5ab..0b5938802a5a37b54ee5ec8ca6155d847207e3b1 100644 (file)
@@ -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;
 
index 8d6667b4053b947ebae18cf38796ae1a5a0c546f..f53971416ec555159def0bff1dede2411877c487 100644 (file)
@@ -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
 
 ############################################################
similarity index 98%
rename from test/udev-test.py
rename to test/test-udev.py
index 5ddda3865f89a36b69aee5a097dccb4f9b585d1a..86937205de6f7a1051d100a188a2ea84390e97d2 100755 (executable)
@@ -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)