Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pytests fails 2.4.0 if systemd-hwdb is missing #515

Closed
motorto opened this issue Aug 3, 2022 · 12 comments · Fixed by #543
Closed

Pytests fails 2.4.0 if systemd-hwdb is missing #515

motorto opened this issue Aug 3, 2022 · 12 comments · Fixed by #543

Comments

@motorto
Copy link

motorto commented Aug 3, 2022

Trying to build from source the libwacom-2.4.0, but the 8th test 8/8 libwacom:all / pytest keeps failling without giving much help.

x86_64-glibc voidlinux

Below is the testlog.txt (I have removed the output of the other tests and only kept the 8th one).

I dont know what else to provide, so if I am missing something please tell me so.

testlog.txt
Log of Meson test suite run on 2022-08-03T12:45:44.604501

Inherited environment: SHELL=/bin/sh XBPS_ENDIAN=le XBPS_TARGET_ENDIAN=le XBPS_CFLAGS='-O2 -pipe' KERNEL_SITE=https://www.kernel.org/pub/linux XBPS_UHELPER_XCMD=xbps-uhelper CPPFLAGS_FOR_BUILD='' XBPS_ORIG_MAKEJOBS=4 LDFLAGS_FOR_BUILD='' XBPS_ALLOW_RESTRICTED=yes XBPS_TARGET_WORDSIZE=64 XBPS_CMPVER_CMD='xbps-uhelper cmpver' BUILD_LDFLAGS='' XBPS_VERSION=' 0.59.1 ' CPP=cpp XBPS_WORDSIZE=64 XBPS_SHUTILSDIR=/void-packages/common/xbps-src/shutils LD_FOR_BUILD=ld FREEDESKTOP_SITE=https://freedesktop.org/software XBPS_REMOVE_XCMD=xbps-remove XBPS_RINDEX_XCMD=xbps-rindex BUILD_CXX=c++ FFLAGS='-fstack-clash-protection -mtune=generic -O2 -pipe   ' XBPS_SRCDISTDIR=/host/sources XBPS_INSTALL_CMD='xbps-install -c /host/repocache-x86_64' PWD=/builddir/libwacom-2.4.0/build XBPS_BUILDSTYLEDIR=/void-packages/common/build-style SOURCE_DATE_EPOCH=1659500892 XBPS_BUILDHELPERDIR=/void-packages/common/build-helper XBPS_LDFLAGS='' XBPS_MASTERDIR=/ XBPS_DESTDIR=/destdir CFLAGS_FOR_BUILD='-O2 -pipe' XBPS_LIBEXECDIR=/void-packages/common/xbps-src/libexec XBPS_RECONFIGURE_CMD=xbps-reconfigure RUST_BUILD=x86_64-unknown-linux-gnu CXX=g++ CXXFLAGS='-fstack-clash-protection -D_FORTIFY_SOURCE=2 -mtune=generic -O2 -pipe    -fdebug-prefix-map=/builddir/libwacom-2.4.0=.' XBPS_RECONFIGURE_XCMD=xbps-reconfigure _=/usr/bin/meson BUILD_CC=cc GNU_SITE=https://ftp.gnu.org/gnu BUILD_FC=gfortran XBPS_TARGET_MACHINE=x86_64 XBPS_QUERY_CMD='xbps-query -c /host/repocache-x86_64' BUILD_FFLAGS='-O2 -pipe' LDFLAGS='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed    ' XBPS_CPPFLAGS='' HOME=/tmp LANG=en_US.UTF-8 XBPS_UHELPER_CMD=xbps-uhelper XBPS_DISTDIR=/void-packages DEBIAN_SITE=http://ftp.debian.org/debian/pool CXX_FOR_BUILD=g++ SOURCEFORGE_SITE=https://downloads.sourceforge.net/sourceforge XBPS_LIBC=glibc XBPS_RINDEX_CMD=xbps-rindex XBPS_MAKEJOBS=4 XBPS_BUILDDIR=/builddir CPPFLAGS='   ' BUILD_CXXFLAGS='-O2 -pipe' LD=ld XBPS_CHECK_PKGS=yes CCACHE_BASEDIR=/builddir/libwacom-2.4.0/ GNOME_SITE=https://download.gnome.org/sources CPP_FOR_BUILD=cpp NONGNU_SITE=https://download.savannah.nongnu.org/releases READELF=readelf XBPS_NO_ATOMIC8='' QT_QPA_PLATFORM=offscreen CHROOT_READY=1 FC_FOR_BUILD=gfortran BUILD_CFLAGS='-O2 -pipe' XBPS_TARGET_NO_ATOMIC8='' MOZILLA_SITE=https://ftp.mozilla.org/pub XBPS_HOSTDIR=/host XBPS_GIT_CMD=/usr/bin/chroot-git XBPS_CROSSPFDIR=/void-packages/common/cross-profiles CC_FOR_BUILD=cc IN_CHROOT=1 KDE_SITE=https://download.kde.org/stable XBPS_FFLAGS='-O2 -pipe' FFLAGS_FOR_BUILD='-O2 -pipe' VIDEOLAN_SITE=https://download.videolan.org/pub/videolan XBPS_QUERY_XCMD='xbps-query -c /host/repocache-x86_64' AR=ar AS=as CXXFLAGS_FOR_BUILD='-O2 -pipe' XBPS_ALLOW_CHROOT_BREAKOUT='' SHLVL=3 XBPS_CONFIG_FILE=/etc/xbps/xbps-src.conf XORG_SITE=https://www.x.org/releases/individual NM=nm GCC=cc PKG_CONFIG_FOR_BUILD=/usr/bin/pkg-config BUILD_CPPFLAGS='' RUST_TARGET=x86_64-unknown-linux-gnu XBPS_SRC_VERSION=113 XBPS_REMOVE_CMD=xbps-remove XBPS_ALT_REPOSITORY=libwacom-2.4.0 XBPS_MACHINE=x86_64 PYPI_SITE=https://files.pythonhosted.org/packages/source OBJCOPY=objcopy XBPS_TARGET_LIBC=glibc XBPS_TRIGGERSDIR=/void-packages/srcpkgs/xbps-triggers/files FC=gfortran XBPS_CXXFLAGS='-O2 -pipe' XBPS_TARGET=pkg CPAN_SITE=https://www.cpan.org/modules/by-module STRIP=strip XBPS_DIGEST_CMD=xbps-digest LC_COLLATE=C OBJDUMP=objdump PATH=/usr/bin:/home/cerqueira/.config/rofi/menus:/home/cerqueira/.config/bspwm/scripts/:/home/cerqueira/.local/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/texlive/2022/bin/x86_64-linux:/home/cerqueira/git/void-packages/masterdir/usr/bin XBPS_FETCH_CMD=xbps-fetch CC=cc XBPS_SRCPKGDIR=/void-packages/srcpkgs CFLAGS='-fstack-clash-protection -D_FORTIFY_SOURCE=2 -mtune=generic -O2 -pipe    -fdebug-prefix-map=/builddir/libwacom-2.4.0=.' XBPS_COMMONDIR=/void-packages/common BUILD_CPP=cpp XBPS_WRAPPERDIR=/builddir/.xbps-libwacom/wrappers XBPS_CHECKVERS_CMD=xbps-checkvers XBPS_REPOSITORY=/host/binpkgs/libwacom-2.4.0 XBPS_STATEDIR=/builddir/.xbps-libwacom XBPS_ARCH=x86_64 XBPS_INSTALL_XCMD='xbps-install -c /host/repocache-x86_64' RANLIB=ranlib UBUNTU_SITE=http://archive.ubuntu.com/ubuntu/pool OLDPWD=/builddir/libwacom-2.4.0 BUILD_LD=ld PKG_CONFIG=pkg-config 

1/8 libwacom:all / files-in-git                  SKIP            0.02s   exit status 77
12:45:44 MALLOC_PERTURB_=254 /builddir/libwacom-2.4.0/test/check-files-in-git.sh /builddir/libwacom-2.4.0
----------------------------------- output -----------------------------------
Not a git tree. Skipping
------------------------------------------------------------------------------

8/8 libwacom:all / pytest                        TIMEOUT        30.01s   killed by signal 15 SIGTERM
12:45:44 MALLOC_PERTURB_=154 MESON_SOURCE_ROOT=/builddir/libwacom-2.4.0 /usr/bin/pytest /builddir/libwacom-2.4.0
----------------------------------- output -----------------------------------
============================= test session starts ==============================
platform linux -- Python 3.10.5, pytest-7.1.2, pluggy-1.0.0
rootdir: /builddir/libwacom-2.4.0
collected 1796 items

../test/test_data_files.py ............................................. [  2%]
........................................................................ [  6%]
........................................................................ [ 10%]
........................................................................ [ 14%]
........................................................................ [ 18%]
........................................................................ [ 22%]
........................................................................ [ 26%]
........................................................................ [ 30%]
........................................................................ [ 34%]
........................................................................ [ 38%]
........................................................................ [ 42%]
........................................................................ [ 46%]
........................................................................ [ 50%]
........................................................................ [ 54%]
........................................................................ [ 58%]
........................................................................ [ 62%]
........................................................................ [ 66%]
........................................................................ [ 70%]
........................................................................ [ 74%]
...................................................................      [ 78%]
../test/test_udev_rules.py EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE [ 80%]
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE [ 84%]
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE [ 88%]
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE [ 92%]
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE [ 96%]
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
------------------------------------------------------------------------------


Summary of Failures:

8/8 libwacom:all / pytest                        TIMEOUT        30.01s   killed by signal 15 SIGTERM


Ok:                 6   
Expected Fail:      0   
Fail:               0   
Unexpected Pass:    0   
Skipped:            1   
Timeout:            1   

@whot
Copy link
Member

whot commented Aug 3, 2022

If you run pytest -vv then it should give you better output and at least list the tests that fail. I've added this to meson now in #517, but you can run it manually too.

But it looks like these are all the udev tests that are failing. Do you have an alternative udev installed? Can't remember what void does by default. The test itself is very (too?) simple, see the code, so if you're not running a stock standard udev installation, there are probably cracks to fall through. Happy to fix these where necessary.

@motorto
Copy link
Author

motorto commented Aug 4, 2022

Void Linux is using eudev. so it should be fine I think. I will try to debug the test and see where it fails (it seems to timeout after 30 secs)

@Apteryks
Copy link

I suspect Void Linux, like GNU Guix, is not a systemd distribution. On GNU Guix the udev tests fail like so:

$ pytest -vv -k 'test_hwdb_files[tablet0]' test/test_udev_rules.py 
=============================================================================================== test session starts ================================================================================================
platform linux -- Python 3.9.9, pytest-6.2.5, py-1.10.0, pluggy-0.13.1 -- /gnu/store/slsh0qjv5j68xda2bb6h8gsxwyi1j25a-python-wrapper-3.9.9/bin/python
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/tmp/guix-build-libwacom-2.4.0.drv-0/libwacom-2.4.0/.hypothesis/examples')
rootdir: /tmp/guix-build-libwacom-2.4.0.drv-0/libwacom-2.4.0
plugins: hypothesis-6.0.2
collected 388 items / 387 deselected / 1 selected                                                                                                                                                                  

test/test_udev_rules.py::test_hwdb_files[tablet0] ERROR                                                                                                                                                      [100%]

====================================================================================================== ERRORS ======================================================================================================
____________________________________________________________________________________ ERROR at setup of test_hwdb_files[tablet0] ____________________________________________________________________________________

    @pytest.fixture(scope='session', autouse=True)
    def systemd_reload():
        '''Make sure our hwdb and udev rules are up-to-date'''
        import subprocess
>       subprocess.run(['systemd-hwdb', 'update'])

test/test_udev_rules.py:26: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/gnu/store/65i3nhcwmz0p8rqbg48gaavyky4g4hwk-python-3.9.9/lib/python3.9/subprocess.py:505: in run
    with Popen(*popenargs, **kwargs) as process:
/gnu/store/65i3nhcwmz0p8rqbg48gaavyky4g4hwk-python-3.9.9/lib/python3.9/subprocess.py:951: in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Popen: returncode: 255 args: ['systemd-hwdb', 'update']>, args = ['systemd-hwdb', 'update'], executable = b'systemd-hwdb', preexec_fn = None, close_fds = True, pass_fds = (), cwd = None, env = None
startupinfo = None, creationflags = 0, shell = False, p2cread = -1, p2cwrite = -1, c2pread = -1, c2pwrite = -1, errread = -1, errwrite = -1, restore_signals = True, gid = None, gids = None, uid = None, umask = -1
start_new_session = False

    def _execute_child(self, args, executable, preexec_fn, close_fds,
                       pass_fds, cwd, env,
                       startupinfo, creationflags, shell,
                       p2cread, p2cwrite,
                       c2pread, c2pwrite,
                       errread, errwrite,
                       restore_signals,
                       gid, gids, uid, umask,
                       start_new_session):
        """Execute program (POSIX version)"""
    
        if isinstance(args, (str, bytes)):
            args = [args]
        elif isinstance(args, os.PathLike):
            if shell:
                raise TypeError('path-like args is not allowed when '
                                'shell is true')
            args = [args]
        else:
            args = list(args)
    
        if shell:
            # On Android the default shell is at '/system/gnu/store/4y5m9lb8k3qkb1y9m02sw9w9a6hacd16-bash-minimal-5.1.8/bin/sh'.
            unix_shell = ('/system/gnu/store/4y5m9lb8k3qkb1y9m02sw9w9a6hacd16-bash-minimal-5.1.8/bin/sh' if
                      hasattr(sys, 'getandroidapilevel') else '/gnu/store/4y5m9lb8k3qkb1y9m02sw9w9a6hacd16-bash-minimal-5.1.8/bin/sh')
            args = [unix_shell, "-c"] + args
            if executable:
                args[0] = executable
    
        if executable is None:
            executable = args[0]
    
        sys.audit("subprocess.Popen", executable, args, cwd, env)
    
        if (_USE_POSIX_SPAWN
                and os.path.dirname(executable)
                and preexec_fn is None
                and not close_fds
                and not pass_fds
                and cwd is None
                and (p2cread == -1 or p2cread > 2)
                and (c2pwrite == -1 or c2pwrite > 2)
                and (errwrite == -1 or errwrite > 2)
                and not start_new_session
                and gid is None
                and gids is None
                and uid is None
                and umask < 0):
            self._posix_spawn(args, executable, env, restore_signals,
                              p2cread, p2cwrite,
                              c2pread, c2pwrite,
                              errread, errwrite)
            return
    
        orig_executable = executable
    
        # For transferring possible exec failure from child to parent.
        # Data format: "exception name:hex errno:description"
        # Pickle is not used; it is complex and involves memory allocation.
        errpipe_read, errpipe_write = os.pipe()
        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
        low_fds_to_close = []
        while errpipe_write < 3:
            low_fds_to_close.append(errpipe_write)
            errpipe_write = os.dup(errpipe_write)
        for low_fd in low_fds_to_close:
            os.close(low_fd)
        try:
            try:
                # We must avoid complex work that could involve
                # malloc or free in the child process to avoid
                # potential deadlocks, thus we do all this here.
                # and pass it to fork_exec()
    
                if env is not None:
                    env_list = []
                    for k, v in env.items():
                        k = os.fsencode(k)
                        if b'=' in k:
                            raise ValueError("illegal environment variable name")
                        env_list.append(k + b'=' + os.fsencode(v))
                else:
                    env_list = None  # Use execv instead of execve.
                executable = os.fsencode(executable)
                if os.path.dirname(executable):
                    executable_list = (executable,)
                else:
                    # This matches the behavior of os._execvpe().
                    executable_list = tuple(
                        os.path.join(os.fsencode(dir), executable)
                        for dir in os.get_exec_path(env))
                fds_to_keep = set(pass_fds)
                fds_to_keep.add(errpipe_write)
                self.pid = _posixsubprocess.fork_exec(
                        args, executable_list,
                        close_fds, tuple(sorted(map(int, fds_to_keep))),
                        cwd, env_list,
                        p2cread, p2cwrite, c2pread, c2pwrite,
                        errread, errwrite,
                        errpipe_read, errpipe_write,
                        restore_signals, start_new_session,
                        gid, gids, uid, umask,
                        preexec_fn)
                self._child_created = True
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_write)
    
            self._close_pipe_fds(p2cread, p2cwrite,
                                 c2pread, c2pwrite,
                                 errread, errwrite)
    
            # Wait for exec to fail or succeed; possibly raising an
            # exception (limited in size)
            errpipe_data = bytearray()
            while True:
                part = os.read(errpipe_read, 50000)
                errpipe_data += part
                if not part or len(errpipe_data) > 50000:
                    break
        finally:
            # be sure the FD is closed no matter what
            os.close(errpipe_read)
    
        if errpipe_data:
            try:
                pid, sts = os.waitpid(self.pid, 0)
                if pid == self.pid:
                    self._handle_exitstatus(sts)
                else:
                    self.returncode = sys.maxsize
            except ChildProcessError:
                pass
    
            try:
                exception_name, hex_errno, err_msg = (
                        errpipe_data.split(b':', 2))
                # The encoding here should match the encoding
                # written in by the subprocess implementations
                # like _posixsubprocess
                err_msg = err_msg.decode()
            except ValueError:
                exception_name = b'SubprocessError'
                hex_errno = b'0'
                err_msg = 'Bad exception data from child: {!r}'.format(
                              bytes(errpipe_data))
            child_exception_type = getattr(
                    builtins, exception_name.decode('ascii'),
                    SubprocessError)
            if issubclass(child_exception_type, OSError) and hex_errno:
                errno_num = int(hex_errno, 16)
                child_exec_never_called = (err_msg == "noexec")
                if child_exec_never_called:
                    err_msg = ""
                    # The error must be from chdir(cwd).
                    err_filename = cwd
                else:
                    err_filename = orig_executable
                if errno_num != 0:
                    err_msg = os.strerror(errno_num)
>               raise child_exception_type(errno_num, err_msg, err_filename)
E               FileNotFoundError: [Errno 2] No such file or directory: 'systemd-hwdb'

/gnu/store/65i3nhcwmz0p8rqbg48gaavyky4g4hwk-python-3.9.9/lib/python3.9/subprocess.py:1821: FileNotFoundError

I'll try neutralizing that autouse fixture, it may not be necessary.

@Apteryks
Copy link

Apteryks commented Aug 29, 2022

After turning the missing commands in the system_reload fixture to true to neutralize them, all the udev tests were skipped. Removing the exception handling, this is the reason:

test/test_udev_rules.py::test_hwdb_files[tablet0] ERROR                                                                                                                                                      [100%]

====================================================================================================== ERRORS ======================================================================================================
____________________________________________________________________________________ ERROR at setup of test_hwdb_files[tablet0] ____________________________________________________________________________________

tablet = <test_udev_rules.pytest_generate_tests.<locals>.Tablet object at 0x7f9de1819100>

    @pytest.fixture
    def uinput(tablet):
        dev = libevdev.Device()
        dev.name = tablet.name
        dev.id = {
            'vendor': tablet.vid,
            'product': tablet.pid,
            'bustype': tablet.bus
        }
        # Our rules match on pid/vid, so purposely make this look like a
        # non-tablet to verify that our rules apply anyway and not others
        dev.enable(libevdev.EV_REL.REL_X)
        dev.enable(libevdev.EV_REL.REL_Y)
        dev.enable(libevdev.EV_KEY.BTN_LEFT)
        dev.enable(libevdev.EV_KEY.BTN_RIGHT)
    
>       uinput = dev.create_uinput_device()

test/test_udev_rules.py:101: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/gnu/store/fgay3vpx55ky2ma8ypv1rsplamwcgp1q-python-libevdev-0.11/lib/python3.9/site-packages/libevdev/device.py:764: in create_uinput_device
    d._uinput = UinputDevice(self._libevdev, uinput_fd)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <libevdev._clib.UinputDevice object at 0x7f9de1646730>, source = <libevdev._clib.Libevdev object at 0x7f9de1646340>, fileobj = None

    def __init__(self, source, fileobj=None):
        """
        Create a new uinput device based on the source libevdev device. The
        uinput device will mirror all capabilities from the source device.
    
        :param source: A libevdev device with all capabilities set.
        :param fileobj: A file-like object to the /dev/uinput node. If None,
        libevdev will open the device in managed mode. See the libevdev
        documentation for details.
        """
        super(UinputDevice, self).__init__()
    
        self._fileobj = fileobj
        if fileobj is None:
            fd = -2  # UINPUT_OPEN_MANAGED
        else:
            fd = fileobj.fileno()
    
        self._uinput_device = ctypes.POINTER(_UinputDevice)()
        rc = self._uinput_create_from_device(source._ctx, fd, ctypes.byref(self._uinput_device))
        if rc != 0:
>           raise OSError(-rc, os.strerror(-rc))
E           PermissionError: [Errno 13] Permission denied

/gnu/store/fgay3vpx55ky2ma8ypv1rsplamwcgp1q-python-libevdev-0.11/lib/python3.9/site-packages/libevdev/_clib.py:964: PermissionError
============================================================================================= short test summary info ==============================================================================================
ERROR test/test_udev_rules.py::test_hwdb_files[tablet0] - PermissionError: [Errno 13] Permission denied

Permission errors. I'll just disable that test for now. I guess the test requires to be run as root, which is not the case in the Guix build container.

@whot
Copy link
Member

whot commented Aug 30, 2022

I guess the test requires to be run as root

yes, the tests need to create uinput devices but the uinput() fixture does raise pytest.skip() where that fails. I don't know why the condition doesn't catch the exception in your case, that'd be one thing to debug first.

And the same is true for the systemd_reload fixture, best to raise pytest.skip where that fails and thus have it autoskip the tests.

@Apteryks
Copy link

@whot sorry for being unclear, I was experimenting and had removed the exception handling to see what the exceptions were, curious to see if I could get the test to work instead of just being skipped.

@motorto
Copy link
Author

motorto commented Aug 31, 2022

Hum @whot the tests depend on systemd and being root ?

@whot
Copy link
Member

whot commented Aug 31, 2022

effectively yes, the tests depend on being able to create uinput devices and reloading the hwdb. both of those usually require you to be root.

@Apteryks
Copy link

Apteryks commented Sep 3, 2022

OK, about the test being skipped, there is an error after all when not patching anything:

building /gnu/store/9ayyjrxsrs1ac076qq772qwamw4v7dyp-baobab-42.0.drv...
 6/12 accountsservice:accounts-service+libaccountsservice+TestAccountsService / TestAccountsService.test_delete_non_existent_user OK              5.73s
derivation '/gnu/store/3bjcsygshjc3h14vin5q9k2180f0nyka-libwacom-2.4.0.drv' offloaded to 'localhost' failed: build of `/gnu/store/3bjcsygshjc3h14vin5q9k2180f0nyka-libwacom-2.4.0.drv' failed
7/8 libwacom:all / pytest                        FAIL           29.60s   exit status 1
>>> MESON_SOURCE_ROOT=/tmp/guix-build-libwacom-2.4.0.drv-0/libwacom-2.4.0 MALLOC_PERTURB_=8 /gnu/store/7frqm5ijy66f81hr8i1j6791k84lds9w-python-pytest-6.2.5/bin/pytest /tmp/guix-build-libwacom-2.4.0.drv-0/libwacom-2.4.0
――――――――――――――――――――――――――――――――――――― ✀  ―――――――――――――――――――――――――――――――――――――
Listing only the last 100 lines from a long log.
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet289]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet290]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet291]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet292]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet293]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet294]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet295]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet296]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet297]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet298]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet299]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet300]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet301]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet302]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet303]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet304]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet305]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet306]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet307]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet308]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet309]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet310]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet311]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet312]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet313]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet314]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet315]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet316]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet317]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet318]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet319]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet320]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet321]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet322]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet323]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet324]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet325]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet326]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet327]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet328]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet329]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet330]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet331]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet332]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet333]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet334]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet335]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet336]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet337]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet338]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet339]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet340]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet341]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet342]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet343]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet344]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet345]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet346]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet347]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet348]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet349]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet350]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet351]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet352]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet353]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet354]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet355]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet356]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet357]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet358]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet359]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet360]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet361]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet362]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet363]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet364]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet365]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet366]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet367]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet368]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet369]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet370]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet371]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet372]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet373]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet374]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet375]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet376]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet377]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet378]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet379]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet380]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet381]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet382]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet383]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet384]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet385]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet386]
ERROR ../libwacom-2.4.0/test/test_udev_rules.py::test_hwdb_files[tablet387]
====================== 1408 passed, 388 errors in 28.89s =======================

I needed to restablish this patch:

(add-after 'unpack 'fix-tests
(lambda _
  ;; Do not attempt to run systemd-specific commands.
  (substitute* "test/test_udev_rules.py"
    (("(systemd-hwdb|systemctl)")
     "true"))))

To have the problematic tests skipped.

@whot
Copy link
Member

whot commented Sep 4, 2022

the meson testlog would have a more detailed explanation of what's going on, the above is just "it failed" but doesn't include any information. You could run pytest -vv --log-level=DEBUG -x directly to get some more info and reduce the logspam (see the pytest help for those flags).

@Apteryks
Copy link

Here's what the failure looks like when I remove my patch:

test/test_udev_rules.py::test_hwdb_files[tablet0] ERROR                  [ 78%]

==================================== ERRORS ====================================
__________________ ERROR at setup of test_hwdb_files[tablet0] __________________

    @pytest.fixture(scope='session', autouse=True)
    def systemd_reload():
        '''Make sure our hwdb and udev rules are up-to-date'''
        import subprocess
>       subprocess.run(['systemd-hwdb', 'update'])

test/test_udev_rules.py:26: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/gnu/store/65i3nhcwmz0p8rqbg48gaavyky4g4hwk-python-3.9.9/lib/python3.9/subprocess.py:505: in run
    with Popen(*popenargs, **kwargs) as process:
/gnu/store/65i3nhcwmz0p8rqbg48gaavyky4g4hwk-python-3.9.9/lib/python3.9/subprocess.py:951: in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <Popen: returncode: 255 args: ['systemd-hwdb', 'update']>
args = ['systemd-hwdb', 'update'], executable = b'systemd-hwdb'
preexec_fn = None, close_fds = True, pass_fds = (), cwd = None, env = None
startupinfo = None, creationflags = 0, shell = False, p2cread = -1
p2cwrite = -1, c2pread = -1, c2pwrite = -1, errread = -1, errwrite = -1
restore_signals = True, gid = None, gids = None, uid = None, umask = -1
start_new_session = False

    def _execute_child(self, args, executable, preexec_fn, close_fds,
                       pass_fds, cwd, env,
                       startupinfo, creationflags, shell,
                       p2cread, p2cwrite,
                       c2pread, c2pwrite,
                       errread, errwrite,
                       restore_signals,
                       gid, gids, uid, umask,
                       start_new_session):
        """Execute program (POSIX version)"""
    
        if isinstance(args, (str, bytes)):
            args = [args]
        elif isinstance(args, os.PathLike):
            if shell:
                raise TypeError('path-like args is not allowed when '
                                'shell is true')
            args = [args]
        else:
            args = list(args)
    
        if shell:
            # On Android the default shell is at '/system/gnu/store/4y5m9lb8k3qkb1y9m02sw9w9a6hacd16-bash-minimal-5.1.8/bin/sh'.
            unix_shell = ('/system/gnu/store/4y5m9lb8k3qkb1y9m02sw9w9a6hacd16-bash-minimal-5.1.8/bin/sh' if
                      hasattr(sys, 'getandroidapilevel') else '/gnu/store/4y5m9lb8k3qkb1y9m02sw9w9a6hacd16-bash-minimal-5.1.8/bin/sh')
            args = [unix_shell, "-c"] + args
            if executable:
                args[0] = executable
    
        if executable is None:
            executable = args[0]
    
        sys.audit("subprocess.Popen", executable, args, cwd, env)
    
        if (_USE_POSIX_SPAWN
                and os.path.dirname(executable)
                and preexec_fn is None
                and not close_fds
                and not pass_fds
                and cwd is None
                and (p2cread == -1 or p2cread > 2)
                and (c2pwrite == -1 or c2pwrite > 2)
                and (errwrite == -1 or errwrite > 2)
                and not start_new_session
                and gid is None
                and gids is None
                and uid is None
                and umask < 0):
            self._posix_spawn(args, executable, env, restore_signals,
                              p2cread, p2cwrite,
                              c2pread, c2pwrite,
                              errread, errwrite)
            return
    
        orig_executable = executable
    
        # For transferring possible exec failure from child to parent.
        # Data format: "exception name:hex errno:description"
        # Pickle is not used; it is complex and involves memory allocation.
        errpipe_read, errpipe_write = os.pipe()
        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
        low_fds_to_close = []
        while errpipe_write < 3:
            low_fds_to_close.append(errpipe_write)
            errpipe_write = os.dup(errpipe_write)
        for low_fd in low_fds_to_close:
            os.close(low_fd)
        try:
            try:
                # We must avoid complex work that could involve
                # malloc or free in the child process to avoid
                # potential deadlocks, thus we do all this here.
                # and pass it to fork_exec()
    
                if env is not None:
                    env_list = []
                    for k, v in env.items():
                        k = os.fsencode(k)
                        if b'=' in k:
                            raise ValueError("illegal environment variable name")
                        env_list.append(k + b'=' + os.fsencode(v))
                else:
                    env_list = None  # Use execv instead of execve.
                executable = os.fsencode(executable)
                if os.path.dirname(executable):
                    executable_list = (executable,)
                else:
                    # This matches the behavior of os._execvpe().
                    executable_list = tuple(
                        os.path.join(os.fsencode(dir), executable)
                        for dir in os.get_exec_path(env))
                fds_to_keep = set(pass_fds)
                fds_to_keep.add(errpipe_write)
                self.pid = _posixsubprocess.fork_exec(
                        args, executable_list,
                        close_fds, tuple(sorted(map(int, fds_to_keep))),
                        cwd, env_list,
                        p2cread, p2cwrite, c2pread, c2pwrite,
                        errread, errwrite,
                        errpipe_read, errpipe_write,
                        restore_signals, start_new_session,
                        gid, gids, uid, umask,
                        preexec_fn)
                self._child_created = True
            finally:
                # be sure the FD is closed no matter what
                os.close(errpipe_write)
    
            self._close_pipe_fds(p2cread, p2cwrite,
                                 c2pread, c2pwrite,
                                 errread, errwrite)
    
            # Wait for exec to fail or succeed; possibly raising an
            # exception (limited in size)
            errpipe_data = bytearray()
            while True:
                part = os.read(errpipe_read, 50000)
                errpipe_data += part
                if not part or len(errpipe_data) > 50000:
                    break
        finally:
            # be sure the FD is closed no matter what
            os.close(errpipe_read)
    
        if errpipe_data:
            try:
                pid, sts = os.waitpid(self.pid, 0)
                if pid == self.pid:
                    self._handle_exitstatus(sts)
                else:
                    self.returncode = sys.maxsize
            except ChildProcessError:
                pass
    
            try:
                exception_name, hex_errno, err_msg = (
                        errpipe_data.split(b':', 2))
                # The encoding here should match the encoding
                # written in by the subprocess implementations
                # like _posixsubprocess
                err_msg = err_msg.decode()
            except ValueError:
                exception_name = b'SubprocessError'
                hex_errno = b'0'
                err_msg = 'Bad exception data from child: {!r}'.format(
                              bytes(errpipe_data))
            child_exception_type = getattr(
                    builtins, exception_name.decode('ascii'),
                    SubprocessError)
            if issubclass(child_exception_type, OSError) and hex_errno:
                errno_num = int(hex_errno, 16)
                child_exec_never_called = (err_msg == "noexec")
                if child_exec_never_called:
                    err_msg = ""
                    # The error must be from chdir(cwd).
                    err_filename = cwd
                else:
                    err_filename = orig_executable
                if errno_num != 0:
                    err_msg = os.strerror(errno_num)
>               raise child_exception_type(errno_num, err_msg, err_filename)
E               FileNotFoundError: [Errno 2] No such file or directory: 'systemd-hwdb'

/gnu/store/65i3nhcwmz0p8rqbg48gaavyky4g4hwk-python-3.9.9/lib/python3.9/subprocess.py:1821: FileNotFoundError
=========================== short test summary info ============================
ERROR test/test_udev_rules.py::test_hwdb_files[tablet0] - FileNotFoundError: ...
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
======================== 1408 passed, 1 error in 1.65s =========================
error: in phase 'check': uncaught exception:
%exception #<&invoke-error program: "pytest" arguments: ("-vv" "--log-level=DEBUG" "-x") exit-status: 1 term-signal: #f stop-signal: #f> 

@whot
Copy link
Member

whot commented Nov 16, 2022

subprocess.run(['systemd-hwdb', 'update'])

this runs systemd-hwdb, guess we need some check for that to exist and raise a skip exception if not. Happy to take a PR for that.

@whot whot changed the title Pytests fails 2.4.0 Pytests fails 2.4.0 if systemd-hwdb is missing Nov 18, 2022
triallax added a commit to triallax/libwacom that referenced this issue Jan 11, 2023
@whot whot closed this as completed in #543 Jan 11, 2023
whot pushed a commit that referenced this issue Jan 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants