gparted/tests/test_BlockSpecial.cc

494 lines
16 KiB
C++
Raw Normal View History

/* Copyright (C) 2017 Mike Fleetwood
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/* Test BlockSpecial
*
* In order to make the tests runable by non-root users in a wide
* variety of environments as possible the tests try to assume as small
* a set of available files as possible and discovers used block device
* names and symbolic link name. The following files are explicitly
* needed for the tests:
*
* Name Access Note
* ----------------------- ------- -----
* / Stat
* /proc/partitions Stat
* Read To find any two block
* devices
* /dev/BLOCK0 Stat First entry from
* /proc/partitions
* /dev/BLOCK1 stat Second entry from
* /proc/partitions
* /dev/disk/by-id/ Readdir To find any symlink to a
* block device
* /dev/disk/by-id/SYMLINK Stat First directory entry
* /dev/RBLOCK Stat Device to which SYMLINK
* refers
*
* Other dummy names are pre-loaded into the internal BlockSpecial cache
* and never queried from the file system.
*/
#include "BlockSpecial.h"
#include "gtest/gtest.h"
#include <string>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <sys/types.h>
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
#include <sys/stat.h>
#include <errno.h>
#include <dirent.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <glibmm/ustring.h>
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
namespace GParted
{
// Print method for a BlockSpecial object
std::ostream& operator<<( std::ostream & out, const BlockSpecial & bs )
{
out << "BlockSpecial{\"" << bs.m_name << "\"," << bs.m_major << "," << bs.m_minor << "}";
return out;
}
// Helper to construct and return message for equality assertion used in:
// EXPECT_BSEQTUP( bs, name, major, minor )
// Reference:
// Google Test, AdvancedGuide, Using a Predicate-Formatter
::testing::AssertionResult CompareHelperBS2TUP( const char * bs_expr, const char * name_expr,
const char * major_expr, const char * minor_expr,
const BlockSpecial & bs, const Glib::ustring & name,
unsigned long major, unsigned long minor )
{
if ( bs.m_name == name && bs.m_major == major && bs.m_minor == minor )
return ::testing::AssertionSuccess();
else
return ::testing::AssertionFailure()
<< " Expected: " << bs_expr << "\n"
<< " Which is: " << bs << "\n"
<< "To be equal to: (" << name_expr << "," << major_expr << "," << major_expr << ")";
}
// Nonfatal assertion that BlockSpecial is equal to tuple (name, major, minor)
#define EXPECT_BSEQTUP(bs, name, major, minor) \
EXPECT_PRED_FORMAT4(CompareHelperBS2TUP, bs, name, major, minor)
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
// Helper to print two compared BlockSpecial objects on failure.
// Usage:
// EXPECT_TRUE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
#define ON_FAILURE_WHERE(b1, b2) \
" Where: " << #b1 << " = " << (b1) << "\n" \
" And: " << #b2 << " = " << (b2);
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
#define SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(name) \
{ \
struct stat sb; \
if (stat(name.c_str(), &sb) != 0 && errno == ENOENT) \
{ \
std::cout << __FILE__ << ":" << __LINE__ << ": Skip test. Block " \
<< "device '" << name << "' does not exist" << std::endl; \
return; \
} \
}
// Return block device names numbered 0 upwards like "/dev/sda" by reading entries from
// /proc/partitions.
static std::string get_block_name( unsigned want )
{
std::ifstream proc_partitions( "/proc/partitions" );
if ( ! proc_partitions )
{
ADD_FAILURE() << __func__ << "(): Failed to open '/proc/partitions'";
return "";
}
std::string line;
char name[100] = "";
unsigned entry = 0;
while ( getline( proc_partitions, line ) )
{
if ( sscanf( line.c_str(), "%*u %*u %*u %99s", name ) == 1 )
{
if ( entry == want )
break;
entry ++;
}
}
proc_partitions.close();
if ( entry == want )
return std::string( "/dev/" ) + name;
ADD_FAILURE() << __func__ << "(): Entry " << want << " not found in '/proc/partitions'";
return "";
}
// Return symbolic link to a block device by reading the first entry in the directory
// /dev/disk/by-id/.
static std::string get_link_name()
{
DIR * dir = opendir( "/dev/disk/by-id" );
if ( dir == NULL )
{
ADD_FAILURE() << __func__ << "(): Failed to open directory '/dev/disk/by-id'";
return "";
}
Fix test (dentry->d_name is invalidated by closedir...) (!41) We have to copy the dentry->d_name before calling closedir(). If not, the string points to nothing and the test fails (It does not fail all the time, but only by chance). Confirmed using valgrind. Selected output from running the unit test under valgrind: $ valgrind --track-origins=yes ./test_blockSpecial ==25110== Memcheck, a memory error detector ... ==25110== Command: ./test_BlockSpecial ==25110== Running main() from src/gtest_main.cc [==========] Running 26 tests from 1 test case. [----------] Global test environment set-up. [----------] 26 tests from BlockSpecialTest ... [ RUN ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches ==25110== Invalid read of size 1 ==25110== at 0x4C2C9B2: strlen (vg_replace_strmem.c:458) ==25110== by 0x40E7C4: length (char_traits.h:259) ==25110== by 0x40E7C4: append (basic_string.h:1009) ==25110== by 0x40E7C4: operator+<char, std::char_traits<char>, std::allocator<char> > (basic_string.h:2468) ==25110== by 0x40E7C4: get_link_name (test_BlockSpecial.cc:156) ==25110== by 0x40E7C4: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... =25110== Address 0x1231ea93 is 115 bytes inside a block of size 32,816 free'd ==25110== at 0x4C2ACBD: free (vg_replace_malloc.c:530) ==25110== by 0x9F773AC: closedir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E7AC: get_link_name (test_BlockSpecial.cc:153) ==25110== by 0x40E7AC: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Block was alloc'd at ==25110== at 0x4C29BC3: malloc (vg_replace_malloc.c:299) ==25110== by 0x9F77280: __alloc_dir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E746: get_link_name (test_BlockSpecial.cc:134) ==25110== by 0x40E746: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Invalid read of size 1 ==25110== at 0x4C2E220: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1022) ==25110== by 0x953A997: std::string::append(char const*, unsigned long) (in /usr/lib64/libstdc++.so.6.0.19) ==25110== by 0x40E7D2: append (basic_string.h:1009) ==25110== by 0x40E7D2: operator+<char, std::char_traits<char>, std::allocator<char> > (basic_string.h:2468) ==25110== by 0x40E7D2: get_link_name (test_BlockSpecial.cc:156) ==25110== by 0x40E7D2: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Address 0x1231ea93 is 115 bytes inside a block of size 32,816 free'd ==25110== at 0x4C2ACBD: free (vg_replace_malloc.c:530) ==25110== by 0x9F773AC: closedir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E7AC: get_link_name (test_BlockSpecial.cc:153) ==25110== by 0x40E7AC: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Block was alloc'd at ==25110== at 0x4C29BC3: malloc (vg_replace_malloc.c:299) ==25110== by 0x9F77280: __alloc_dir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E746: get_link_name (test_BlockSpecial.cc:134) ==25110== by 0x40E746: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... [ OK ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches (50 ms) Selected lines from test_BlockSpecial.cc: 132 static std::string get_link_name() 133 { 134 DIR * dir = opendir( "/dev/disk/by-id" ); ... 141 bool found = false; 142 struct dirent * dentry; 143 // Silence GCC [-Wparentheses] warning with double parentheses 144 while ( ( dentry = readdir( dir ) ) ) 145 { 146 if ( strcmp( dentry->d_name, "." ) != 0 && 147 strcmp( dentry->d_name, ". " ) != 0 ) 148 { 149 found = true; 150 break; 151 } 152 } 153 closedir( dir ); 154 155 if ( found ) 156 return std::string( "/dev/disk/by-id/" ) + dentry->d_name; So the memory referred to by dentry was allocated on line 134, freed on 153 and accessed after freed on 156. Closes !41 - Fix test (dentry->d_name is invalidated by closedir...)
2019-05-22 04:00:53 -06:00
std::string name;
bool found = false;
struct dirent * dentry;
// Silence GCC [-Wparentheses] warning with double parentheses
while ( ( dentry = readdir( dir ) ) )
{
if ( strcmp( dentry->d_name, "." ) != 0 &&
strcmp( dentry->d_name, ".." ) != 0 )
{
Fix test (dentry->d_name is invalidated by closedir...) (!41) We have to copy the dentry->d_name before calling closedir(). If not, the string points to nothing and the test fails (It does not fail all the time, but only by chance). Confirmed using valgrind. Selected output from running the unit test under valgrind: $ valgrind --track-origins=yes ./test_blockSpecial ==25110== Memcheck, a memory error detector ... ==25110== Command: ./test_BlockSpecial ==25110== Running main() from src/gtest_main.cc [==========] Running 26 tests from 1 test case. [----------] Global test environment set-up. [----------] 26 tests from BlockSpecialTest ... [ RUN ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches ==25110== Invalid read of size 1 ==25110== at 0x4C2C9B2: strlen (vg_replace_strmem.c:458) ==25110== by 0x40E7C4: length (char_traits.h:259) ==25110== by 0x40E7C4: append (basic_string.h:1009) ==25110== by 0x40E7C4: operator+<char, std::char_traits<char>, std::allocator<char> > (basic_string.h:2468) ==25110== by 0x40E7C4: get_link_name (test_BlockSpecial.cc:156) ==25110== by 0x40E7C4: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... =25110== Address 0x1231ea93 is 115 bytes inside a block of size 32,816 free'd ==25110== at 0x4C2ACBD: free (vg_replace_malloc.c:530) ==25110== by 0x9F773AC: closedir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E7AC: get_link_name (test_BlockSpecial.cc:153) ==25110== by 0x40E7AC: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Block was alloc'd at ==25110== at 0x4C29BC3: malloc (vg_replace_malloc.c:299) ==25110== by 0x9F77280: __alloc_dir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E746: get_link_name (test_BlockSpecial.cc:134) ==25110== by 0x40E746: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Invalid read of size 1 ==25110== at 0x4C2E220: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1022) ==25110== by 0x953A997: std::string::append(char const*, unsigned long) (in /usr/lib64/libstdc++.so.6.0.19) ==25110== by 0x40E7D2: append (basic_string.h:1009) ==25110== by 0x40E7D2: operator+<char, std::char_traits<char>, std::allocator<char> > (basic_string.h:2468) ==25110== by 0x40E7D2: get_link_name (test_BlockSpecial.cc:156) ==25110== by 0x40E7D2: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Address 0x1231ea93 is 115 bytes inside a block of size 32,816 free'd ==25110== at 0x4C2ACBD: free (vg_replace_malloc.c:530) ==25110== by 0x9F773AC: closedir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E7AC: get_link_name (test_BlockSpecial.cc:153) ==25110== by 0x40E7AC: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Block was alloc'd at ==25110== at 0x4C29BC3: malloc (vg_replace_malloc.c:299) ==25110== by 0x9F77280: __alloc_dir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E746: get_link_name (test_BlockSpecial.cc:134) ==25110== by 0x40E746: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... [ OK ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches (50 ms) Selected lines from test_BlockSpecial.cc: 132 static std::string get_link_name() 133 { 134 DIR * dir = opendir( "/dev/disk/by-id" ); ... 141 bool found = false; 142 struct dirent * dentry; 143 // Silence GCC [-Wparentheses] warning with double parentheses 144 while ( ( dentry = readdir( dir ) ) ) 145 { 146 if ( strcmp( dentry->d_name, "." ) != 0 && 147 strcmp( dentry->d_name, ". " ) != 0 ) 148 { 149 found = true; 150 break; 151 } 152 } 153 closedir( dir ); 154 155 if ( found ) 156 return std::string( "/dev/disk/by-id/" ) + dentry->d_name; So the memory referred to by dentry was allocated on line 134, freed on 153 and accessed after freed on 156. Closes !41 - Fix test (dentry->d_name is invalidated by closedir...)
2019-05-22 04:00:53 -06:00
name = dentry->d_name;
found = true;
break;
}
}
closedir( dir );
if ( found )
Fix test (dentry->d_name is invalidated by closedir...) (!41) We have to copy the dentry->d_name before calling closedir(). If not, the string points to nothing and the test fails (It does not fail all the time, but only by chance). Confirmed using valgrind. Selected output from running the unit test under valgrind: $ valgrind --track-origins=yes ./test_blockSpecial ==25110== Memcheck, a memory error detector ... ==25110== Command: ./test_BlockSpecial ==25110== Running main() from src/gtest_main.cc [==========] Running 26 tests from 1 test case. [----------] Global test environment set-up. [----------] 26 tests from BlockSpecialTest ... [ RUN ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches ==25110== Invalid read of size 1 ==25110== at 0x4C2C9B2: strlen (vg_replace_strmem.c:458) ==25110== by 0x40E7C4: length (char_traits.h:259) ==25110== by 0x40E7C4: append (basic_string.h:1009) ==25110== by 0x40E7C4: operator+<char, std::char_traits<char>, std::allocator<char> > (basic_string.h:2468) ==25110== by 0x40E7C4: get_link_name (test_BlockSpecial.cc:156) ==25110== by 0x40E7C4: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... =25110== Address 0x1231ea93 is 115 bytes inside a block of size 32,816 free'd ==25110== at 0x4C2ACBD: free (vg_replace_malloc.c:530) ==25110== by 0x9F773AC: closedir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E7AC: get_link_name (test_BlockSpecial.cc:153) ==25110== by 0x40E7AC: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Block was alloc'd at ==25110== at 0x4C29BC3: malloc (vg_replace_malloc.c:299) ==25110== by 0x9F77280: __alloc_dir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E746: get_link_name (test_BlockSpecial.cc:134) ==25110== by 0x40E746: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Invalid read of size 1 ==25110== at 0x4C2E220: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1022) ==25110== by 0x953A997: std::string::append(char const*, unsigned long) (in /usr/lib64/libstdc++.so.6.0.19) ==25110== by 0x40E7D2: append (basic_string.h:1009) ==25110== by 0x40E7D2: operator+<char, std::char_traits<char>, std::allocator<char> > (basic_string.h:2468) ==25110== by 0x40E7D2: get_link_name (test_BlockSpecial.cc:156) ==25110== by 0x40E7D2: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Address 0x1231ea93 is 115 bytes inside a block of size 32,816 free'd ==25110== at 0x4C2ACBD: free (vg_replace_malloc.c:530) ==25110== by 0x9F773AC: closedir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E7AC: get_link_name (test_BlockSpecial.cc:153) ==25110== by 0x40E7AC: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... ==25110== Block was alloc'd at ==25110== at 0x4C29BC3: malloc (vg_replace_malloc.c:299) ==25110== by 0x9F77280: __alloc_dir (in /usr/lib64/libc-2.17.so) ==25110== by 0x40E746: get_link_name (test_BlockSpecial.cc:134) ==25110== by 0x40E746: GParted::BlockSpecialTest_NamedBlockSpecialObjectBySymlinkMatches_Test::TestBody() (test_BlockSpecial.cc:247) ... [ OK ] BlockSpecialTest.NamedBlockSpecialObjectBySymlinkMatches (50 ms) Selected lines from test_BlockSpecial.cc: 132 static std::string get_link_name() 133 { 134 DIR * dir = opendir( "/dev/disk/by-id" ); ... 141 bool found = false; 142 struct dirent * dentry; 143 // Silence GCC [-Wparentheses] warning with double parentheses 144 while ( ( dentry = readdir( dir ) ) ) 145 { 146 if ( strcmp( dentry->d_name, "." ) != 0 && 147 strcmp( dentry->d_name, ". " ) != 0 ) 148 { 149 found = true; 150 break; 151 } 152 } 153 closedir( dir ); 154 155 if ( found ) 156 return std::string( "/dev/disk/by-id/" ) + dentry->d_name; So the memory referred to by dentry was allocated on line 134, freed on 153 and accessed after freed on 156. Closes !41 - Fix test (dentry->d_name is invalidated by closedir...)
2019-05-22 04:00:53 -06:00
return std::string("/dev/disk/by-id/") + name;
ADD_FAILURE() << __func__ << "(): No entries found in directory '/dev/disk/by-id'";
return "";
}
// Follow symbolic link return real path.
static std::string follow_link_name( std::string link )
{
char * rpath = realpath( link.c_str(), NULL );
if ( rpath == NULL )
{
ADD_FAILURE() << __func__ << "(): Failed to resolve symbolic link '" << link << "'";
return "";
}
std::string rpath_copy = rpath;
free( rpath );
return rpath_copy;
}
TEST( BlockSpecialTest, UnnamedBlockSpecialObject )
{
// Test default constructor produces empty BlockSpecial object ("", 0, 0).
BlockSpecial::clear_cache();
BlockSpecial bs;
EXPECT_BSEQTUP( bs, "", 0, 0 );
}
TEST( BlockSpecialTest, NamedBlockSpecialObjectPlainFile )
{
// Test any named plain file or directory (actually any non-block special name)
// produces BlockSpecial object (name, 0, 0).
BlockSpecial::clear_cache();
BlockSpecial bs( "/" );
EXPECT_BSEQTUP( bs, "/", 0, 0 );
}
TEST( BlockSpecialTest, NamedBlockSpecialObjectPlainFileDuplicate )
{
// Test second constructed BlockSpecial object with the same named plain file or
// directory, where the major and minor numbers are retrieved via internal
// BlockSpecial cache, produce an equivalent object (name, 0, 0).
BlockSpecial::clear_cache();
BlockSpecial bs1( "/" );
BlockSpecial bs2( "/" );
EXPECT_BSEQTUP( bs1, "/", 0, 0 );
EXPECT_BSEQTUP( bs2, "/", 0, 0 );
}
TEST( BlockSpecialTest, NamedBlockSpecialObjectBlockDevice )
{
std::string bname = get_block_name( 0 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname);
// Test any block special name produces BlockSpecial object (name, major, minor).
BlockSpecial::clear_cache();
BlockSpecial bs( bname );
EXPECT_STREQ( bs.m_name.c_str(), bname.c_str() );
EXPECT_TRUE( bs.m_major > 0 || bs.m_minor > 0 );
}
TEST( BlockSpecialTest, NamedBlockSpecialObjectBlockDeviceDuplicate )
{
std::string bname = get_block_name( 0 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname);
// Test that a second object with the same name of a block device produces the
// same (name, major, minor). Checking internal BlockSpecial caching again.
BlockSpecial::clear_cache();
BlockSpecial bs1( bname );
BlockSpecial bs2( bname );
EXPECT_STREQ( bs1.m_name.c_str(), bs2.m_name.c_str() );
EXPECT_EQ( bs1.m_major, bs2.m_major );
EXPECT_EQ( bs1.m_minor, bs2.m_minor );
}
TEST( BlockSpecialTest, TwoNamedBlockSpecialObjectBlockDevices )
{
std::string bname1 = get_block_name( 0 );
std::string bname2 = get_block_name( 1 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname1);
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname2);
// Test that two different named block devices produce different
// (name, major, minor).
BlockSpecial::clear_cache();
BlockSpecial bs1( bname1 );
BlockSpecial bs2( bname2 );
EXPECT_STRNE( bs1.m_name.c_str(), bs2.m_name.c_str() );
EXPECT_TRUE( bs1.m_major != bs2.m_major || bs1.m_minor != bs2.m_minor );
}
TEST( BlockSpecialTest, NamedBlockSpecialObjectBySymlinkMatches )
{
std::string lname = get_link_name();
std::string bname = follow_link_name( lname );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname);
// Test that a symbolic link to a block device and the named block device produce
// BlockSpecial objects with different names but the same major, minor pair.
BlockSpecial::clear_cache();
BlockSpecial lnk( lname );
BlockSpecial bs( bname );
EXPECT_STRNE( lnk.m_name.c_str(), bs.m_name.c_str() );
EXPECT_EQ( lnk.m_major, bs.m_major );
EXPECT_EQ( lnk.m_minor, bs.m_minor );
}
TEST( BlockSpecialTest, PreRegisteredCacheUsedBeforeFileSystem )
{
// Test that the cache is used before querying the file system by registering a
// deliberately wrong major, minor pair for /dev/null and ensuring those are the
// major, minor numbers reported for the device.
BlockSpecial::clear_cache();
BlockSpecial::register_block_special( "/dev/null", 4, 8 );
BlockSpecial bs( "/dev/null" );
EXPECT_BSEQTUP( bs, "/dev/null", 4, 8 );
}
TEST( BlockSpecialTest, OperatorEqualsTwoEmptyObjects )
{
// Test equality of two empty BlockSpecial objects.
BlockSpecial::clear_cache();
BlockSpecial bs1;
BlockSpecial bs2;
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorEqualsSamePlainFiles )
{
// Test equality of two named plain file or directory BlockSpecial objects;
BlockSpecial::clear_cache();
BlockSpecial bs1( "/" );
BlockSpecial bs2( "/" );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorEqualsEmptyObjectAndPlainFile )
{
// Test inequality of empty and plain file BlockSpecial objects.
BlockSpecial::clear_cache();
BlockSpecial bs1;
BlockSpecial bs2( "/" );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_FALSE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorEqualsDifferentPlainFiles )
{
// Test inequality of two different plain file or directory BlockSpecial objects;
BlockSpecial::clear_cache();
BlockSpecial bs1( "/" );
BlockSpecial bs2( "/proc/partitions" );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_FALSE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorEqualsSameBlockDevices )
{
std::string bname = get_block_name( 0 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname);
// Test equality of two BlockSpecial objects using the same named block device.
BlockSpecial::clear_cache();
BlockSpecial bs1( bname );
BlockSpecial bs2( bname );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorEqualsEmptyObjectAndBlockDevice )
{
std::string bname = get_block_name( 0 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname);
// Test inequality of empty and named block device BlockSpecial objects.
BlockSpecial::clear_cache();
BlockSpecial bs1;
BlockSpecial bs2( bname );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_FALSE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorEqualsPlainFileAndBlockDevice )
{
std::string bname = get_block_name( 0 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname);
// Test inequality of plain file and block device BlockSpecial objects.
BlockSpecial::clear_cache();
BlockSpecial bs1( "/" );
BlockSpecial bs2( bname );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_FALSE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorEqualsTwoDifferentBlockDevices )
{
std::string bname1 = get_block_name( 0 );
std::string bname2 = get_block_name( 1 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname1);
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname2);
// Test inequality of two different named block device BlockSpecial objects.
BlockSpecial::clear_cache();
BlockSpecial bs1( bname1 );
BlockSpecial bs2( bname2 );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_FALSE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorEqualsSameBlockDevicesWithMinorZero )
{
// Test for fix in bug #771670. Ensure that block devices with minor number 0 are
// compared by major, minor pair rather than by name.
BlockSpecial::clear_cache();
BlockSpecial::register_block_special( "/dev/mapper/encrypted_swap", 254, 0 );
BlockSpecial::register_block_special( "/dev/dm-0", 254, 0 );
BlockSpecial bs1( "/dev/mapper/encrypted_swap" );
BlockSpecial bs2( "/dev/dm-0" );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 == bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorLessThanTwoEmptyObjects )
{
// Test one empty BlockSpecial object is not ordered before another empty one.
BlockSpecial::clear_cache();
BlockSpecial bs1;
BlockSpecial bs2;
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorLessThanSamePlainFiles )
{
// Test one plain file BlockSpecial object is not ordered before another of the
// same name.
BlockSpecial::clear_cache();
BlockSpecial bs1( "/" );
BlockSpecial bs2( "/" );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorLessThanDifferentPlainFiles )
{
// Test one plain file BlockSpecial object with the lower name is ordered before
// the other plain file with a higher name.
BlockSpecial::clear_cache();
BlockSpecial::register_block_special( "/dummy_file1", 0, 0 );
BlockSpecial::register_block_special( "/dummy_file2", 0, 0 );
BlockSpecial bs1( "/dummy_file1" );
BlockSpecial bs2( "/dummy_file2" );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorLessThanEmptyObjectAndPlainFile )
{
// Test empty BlockSpecial object with name "" is before a plain file of any name.
BlockSpecial::clear_cache();
BlockSpecial bs1;
BlockSpecial bs2( "/" );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorLessThanSameBlockDevices )
{
std::string bname = get_block_name( 0 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname);
// Test one name block device BlockSpecial object is not ordered before another
// from the same name.
BlockSpecial::clear_cache();
BlockSpecial bs1( bname );
BlockSpecial bs2( bname );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorLessThanEmptyObjectAndBlockDevice )
{
std::string bname = get_block_name( 0 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname);
// Test empty BlockSpecial object with name "" is before any block device.
BlockSpecial::clear_cache();
BlockSpecial bs1;
BlockSpecial bs2( bname );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorLessThanPlainFileAndBlockDevice )
{
std::string bname = get_block_name( 0 );
Skip BlockSpecial unit tests when devices don't exist, for CI test images (!113) 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] 57983b9fc20c9367f9dd83a8301e903fced5329e 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-calls Closed !113 - Fix occasional GitLab CI test jobs failures on BlockSpecial unit tests
2023-05-08 01:29:02 -06:00
SKIP_IF_BLOCK_DEVICE_DOESNT_EXIST(bname);
// Test one plain file BlockSpecial object is ordered before any block device.
BlockSpecial::clear_cache();
BlockSpecial bs1( "/" );
BlockSpecial bs2( bname );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorLessThanTwoDifferentBlockDevicesMajorNumbers )
{
// Test one block device BlockSpecial object is ordered before another based on
// major numbers.
BlockSpecial::clear_cache();
BlockSpecial::register_block_special( "/dummy_block2", 1, 1 );
BlockSpecial::register_block_special( "/dummy_block1", 2, 0 );
BlockSpecial bs1( "/dummy_block2" );
BlockSpecial bs2( "/dummy_block1" );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
TEST( BlockSpecialTest, OperatorLessThanTwoDifferentBlockDevicesMinorNumbers )
{
// Test one block device BlockSpecial object is ordered before another based on
// minor numbers.
BlockSpecial::clear_cache();
BlockSpecial::register_block_special( "/dummy_block2", 2, 0 );
BlockSpecial::register_block_special( "/dummy_block1", 2, 1 );
BlockSpecial bs1( "/dummy_block2" );
BlockSpecial bs2( "/dummy_block1" );
Improve diagnostics of failed BlockSpecial operator tests (#781978) Deliberately breaking one of the operator<() tests produces less than ideal diagnostics because it doesn't print the BlockSpecial objects being compared. $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:362: Failure Value of: bs1 < bs2 Actual: true Expected: false [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (1 ms) ... This could be solved by using the Google Test Binary Comparison assertions, however in the tests for false from (bs1 == bs2) and (bs1 < bs2) comparisons then operators != and >= would have to be implemented and the tests changed from: EXPECT_FALSE( bs1 < bs2 ); to: EXPECT_GE( bs1, bs2 ); This makes the meaning of the test less than clear. The primary purpose of the test is to check operator<(), but it is expecting the first BlockSpecial object to be GE (greater than or equal to) than the second, which is calling operator>=() which in turn is testing operator<(). For tests of the operators themselves using Google Test Binary Comparison assertions obscures what is being tested too much. Instead provide a custom failure message which prints the BlockSpecial objects failing the comparison, leaving the test still directly calling the operator being tested, like this: EXPECT_FALSE( bs1 < bs2 ) << " Where: bs1 = " << bs1 << "\n" << " And: bs2 = " << bs2; And with a suitable macro this is simplified to: EXPECT_FALSE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 ); Now the above deliberately broken test produces this output: $ ./test_BlockSpecial ... [ RUN ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects test_BlockSpecial.cc:369: Failure Value of: bs1 < bs2 Actual: true Expected: false Where: bs1 = BlockSpecial{"",0,0} And: bs2 = BlockSpecial{"",0,0} [ FAILED ] BlockSpecialTest.OperatorLessThanTwoEmptyObjects (0 ms) ... Bug 781978 - Add Google Test C++ test framework
2017-05-10 15:42:54 -06:00
EXPECT_TRUE( bs1 < bs2 ) << ON_FAILURE_WHERE( bs1, bs2 );
}
} // namespace GParted