Since November 2022 test_BlockSpecial has been occasionally failing in
GNOME GitLab Docker CI test jobs like this:
[ RUN ] BlockSpecialTest.NamedBlockSpecialObjectBlockDevice
test_BlockSpecial.cc:216: Failure
Value of: bs.m_major > 0 || bs.m_minor > 0
Actual: false
Expected: true
[ FAILED ] BlockSpecialTest.NamedBlockSpecialObjectBlockDevice (0 ms)
...
[ RUN ] BlockSpecialTest.TwoNamedBlockSpecialObjectBlockDevices
test_BlockSpecial.cc:244: Failure
Value of: bs1.m_major != bs2.m_major || bs1.m_minor != bs2.m_minor
Actual: false
Expected: true
[ FAILED ] BlockSpecialTest.TwoNamedBlockSpecialObjectBlockDevices (0 ms)
[ RUN ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches
test_BlockSpecial.cc:170: Failure
Failed
follow_link_name(): Failed to resolve symbolic link '/dev/disk/by-id/gparted-sda'
[ FAILED ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches (0 ms)
...
3 FAILED TESTS
FAIL test_BlockSpecial (exit status: 1)
As identified previously [1] the Docker CI images no longer have any
block devices in /dev. test/makedev.sh script was added to create block
devices test_BlockSpecial needs for it's testing. Now a subset of the
GNOME GitLab job runners additionally prevent creation of block special
device nodes. test/makedev.sh reports this:
$ tests/makedev.sh
mknod -m 0660 /dev/sda b 8 0
mknod: /dev/sda: Operation not permitted
chown: cannot access '/dev/sda': No such file or directory
mknod -m 0660 /dev/sda1 b 8 1
mknod: /dev/sda1: Operation not permitted
chown: cannot access '/dev/sda1': No such file or directory
Alternative rejected solutions:
1. Use fakeroot [2]. Package is available for the 3 distributions used
in CI jobs. Does fake stat() call. Works when run like this in the
CI test jobs:
fakeroot -s test/fakeroot.env tests/makedev.sh
fakeroot -i test/fakeroot.env make check
fakeroot -i test/fakeroot.env make distcheck
But if you run fakeroot ... make check on our development machines
as a non-root user it causes the test_SupportedFileSystems unit
tests which use losetup to fail. This is because
test_SupportedFileSystems thinks it's root inside the fakeroot
environment but fakeroot doesn't fake enough for losetup to work.
This makes running tests in the GitLab CI jobs different from how we
would have to run them on our development machines. Prefer not to
do that.
2. Use GNU ld --wrap [3] to call our own __wrap_stat() allowing
test_BlockSpecial to provide mocked results to the stat() call in
constructor BlockSpecial::BlockSpecial(). This works with
GNU C Library >= 2.33, released 01-Feb-2021, and musl libc,
therefore it works on CI tested distributions Ubuntu LTS >= 22.04
and Alpine Linux respectively. However this fails on earlier glibc
releases, so will fail on CentOS 7 CI image, as the compiler emits a
call to __xstat() rather than stat(). This is something to do with
how glibc's /usr/include/sys/stat.h supported multiple versions of
stat(). Don't use this as it's doesn't work everywhere.
Additional useful implementation hints. [4][5]
Choose to fix by just skipping unit tests which need block special names
to exist in the file system, but don't exist. This is the same
technique that test_SupportedFileSystems uses. So tests/makedev.sh
creates block devices if it can in the GNOME GitLab CI test images [1]
and now if that fails the individual unit tests are skipped.
[1] 57983b9fc2
Create block special devices needed by test_BlockSpecial in GitLab
CI jobs (!59)
[2] FakeRoot
https://wiki.debian.org/FakeRoot
[3] ld(1) - Linux manual page
https://man7.org/linux/man-pages/man1/ld.1.html
"--wrap=symbol
Use a wrapper function for symbol. Any undefined reference to
symbol will be resolved to "__wrap_symbol". Any undefined
reference to "__real_symbol" will be resolved to symbol.
This can be used to provide a wrapper for a system function.
The wrapper function should be called "__wrap_symbol". If it
wishes to call the system function, it should call
"__real_symbol".
...
"
[4] gcc: error: unrecognized option --wrap
https://stackoverflow.com/questions/33278164/gcc-error-unrecognized-option-wrap
[5] C++ ld linker --wrap option does not work for internal function calls
https://stackoverflow.com/questions/44464961/c-ld-linker-wrap-option-does-not-work-for-internal-function-callsClosed!113 - Fix occasional GitLab CI test jobs failures on
BlockSpecial unit tests