gparted/tests/test_EraseFileSystemSignatu...

219 lines
6.4 KiB
C++
Raw Normal View History

Add initial unit test of erase_filesystem_signatures() (#220) Initially just testing erasing of Intel Software RAID signatures. Chosen because it was expected to work, but turned out not to be true in all cases. The code needs to initialise GParted_Core::mainthread, construct Gtk::Main() and execute xvfb-run because of this call chain: GParted_Core::erase_filesystem_signatures() GParted_Core::settle_device() Utils::execute_command ("udevadm settle ...") status.foreground = (Glib::Thread::self() == GParted_Core::mainthread) Gtk::Main::run() This was also needed when testing file system interface classes as discussed in commits [1][2]. The test fails like this: $ ./test_EraseFileSystemSignatures ... [ RUN ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDAligned [ OK ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDAligned (155 ms) [ RUN ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDUnaligned test_EraseFileSystemSignatures.cc:286: Failure Failed image_contains_all_zeros(): First non-zero bytes: 0x00001A00 "Intel Raid ISM C" 49 6E 74 65 6C 20 52 61 69 64 20 49 53 4D 20 43 test_EraseFileSystemSignatures.cc:320: Failure Value of: image_contains_all_zeros() Actual: false Expected: true [ FAILED ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDUnaligned (92 ms) Manually write the same test image: $ python << 'EOF' signature = b'Intel Raid ISM Cfg Sig. ' import os fd = os.open('/tmp/test.img', os.O_CREAT|os.O_WRONLY) os.ftruncate(fd, 16*1024*1024 - 512) os.lseek(fd, -(2*512), os.SEEK_END) os.write(fd, signature) os.close(fd) EOF Run gpartedbin /tmp/test.img and Format to > Cleared. GParted continues to display the the image file as containing an ataraid signature. $ blkid /tmp/test.img /tmp/test.img: TYPE="isw_raid_member" $ hexdump -C /tmp/test.img 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00fffa00 49 6e 74 65 6c 20 52 61 69 64 20 49 53 4d 20 43 |Intel Raid ISM C| 00fffa10 66 67 20 53 69 67 2e 20 00 00 00 00 00 00 00 00 |fg Sig. ........| 00fffa20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00fffe00 This signature is not being cleared when the device/partition/image size is 512 bytes smaller than a whole MiB because the last 3.5 KiB is left unwritten. This is because the last block of zeros written is 8 KiB aligned to 4 KiB at the end of the device. [1] a97c23c57c693b77724970d2f99702d7be18b4bc Add initial create ext2 only FileSystem interface class test (!49) [2] 8db9a83b39b82785dcbcc776ac259aa7986c0955 Run test program under xvfb-run to satisfy need for an X11 display (!49) Closes #220 - Format to Cleared not clearing "pdc" ataraid signature
2023-02-04 08:36:37 -07:00
/* Copyright (C) 2023 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 GParted_Core::erase_filesystem_signatures()
*/
Move duplicated test code into shared modules (#220) Move common testing code which doesn't need linking with GParted objects into the common module. Move the remaining common code used to print GParted objects using the insertion operator (operator<<) into the insertion_operators module. Split the common code like this so that the operator<<(std::ostream&, const OperationDetail&) function is not included in test_PipeCapture and it is not forced to link with all the non-UI related GParted objects. The Automake manual provides guidance that when a header belongs to a single program it is recommended to be listed in the program's _SOURCES variable and for a directory only containing header files listing them in the noinst_HEADERS variable is the right variable to use [1]. However the guidance doesn't cover this case for common.h and insertion_operators.h; header files in a directory with other files and used by multiple programs. So just because we have gparted_core_OBJECTS (normal Makefile, not Automake special variable) listing objects to link with, choose to use noinst_HEADERS Automake variable to list needed headers. [1] GNU Automake manual, 9.2 Header files https://www.gnu.org/software/automake/manual/html_node/Headers.html "Usually, only header files that accompany installed libraries need to be installed. Headers used by programs or convenience libraries are not installed. The noinst_HEADERS variable can be used for such headers. However, when the header belongs to a single convenience library or program, we recommend listing it in the program's or library's _SOURCES variable (see Defining program sources) instead of in noinst_HEADERS. This is clearer for the Makefile.am reader. noinst_HEADERS would be the right variable to use in a directory containing only headers and no associated library or program. All header files must be listed somewhere; in a _SOURCES variable or in a _HEADERS variable. Missing ones will not appear in the distribution. " Closes #220 - Format to Cleared not clearing "pdc" ataraid signature
2023-02-05 04:16:43 -07:00
#include "common.h"
#include "insertion_operators.h"
Add initial unit test of erase_filesystem_signatures() (#220) Initially just testing erasing of Intel Software RAID signatures. Chosen because it was expected to work, but turned out not to be true in all cases. The code needs to initialise GParted_Core::mainthread, construct Gtk::Main() and execute xvfb-run because of this call chain: GParted_Core::erase_filesystem_signatures() GParted_Core::settle_device() Utils::execute_command ("udevadm settle ...") status.foreground = (Glib::Thread::self() == GParted_Core::mainthread) Gtk::Main::run() This was also needed when testing file system interface classes as discussed in commits [1][2]. The test fails like this: $ ./test_EraseFileSystemSignatures ... [ RUN ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDAligned [ OK ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDAligned (155 ms) [ RUN ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDUnaligned test_EraseFileSystemSignatures.cc:286: Failure Failed image_contains_all_zeros(): First non-zero bytes: 0x00001A00 "Intel Raid ISM C" 49 6E 74 65 6C 20 52 61 69 64 20 49 53 4D 20 43 test_EraseFileSystemSignatures.cc:320: Failure Value of: image_contains_all_zeros() Actual: false Expected: true [ FAILED ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDUnaligned (92 ms) Manually write the same test image: $ python << 'EOF' signature = b'Intel Raid ISM Cfg Sig. ' import os fd = os.open('/tmp/test.img', os.O_CREAT|os.O_WRONLY) os.ftruncate(fd, 16*1024*1024 - 512) os.lseek(fd, -(2*512), os.SEEK_END) os.write(fd, signature) os.close(fd) EOF Run gpartedbin /tmp/test.img and Format to > Cleared. GParted continues to display the the image file as containing an ataraid signature. $ blkid /tmp/test.img /tmp/test.img: TYPE="isw_raid_member" $ hexdump -C /tmp/test.img 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00fffa00 49 6e 74 65 6c 20 52 61 69 64 20 49 53 4d 20 43 |Intel Raid ISM C| 00fffa10 66 67 20 53 69 67 2e 20 00 00 00 00 00 00 00 00 |fg Sig. ........| 00fffa20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00fffe00 This signature is not being cleared when the device/partition/image size is 512 bytes smaller than a whole MiB because the last 3.5 KiB is left unwritten. This is because the last block of zeros written is 8 KiB aligned to 4 KiB at the end of the device. [1] a97c23c57c693b77724970d2f99702d7be18b4bc Add initial create ext2 only FileSystem interface class test (!49) [2] 8db9a83b39b82785dcbcc776ac259aa7986c0955 Run test program under xvfb-run to satisfy need for an X11 display (!49) Closes #220 - Format to Cleared not clearing "pdc" ataraid signature
2023-02-04 08:36:37 -07:00
#include "GParted_Core.h"
#include "OperationDetail.h"
#include "Partition.h"
#include "Utils.h"
#include "gtest/gtest.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <gtkmm.h>
#include <parted/parted.h>
#include <glibmm/thread.h>
namespace GParted
{
// Explicit test fixture class for common variables and methods used in each test.
// Reference:
// Google Test, Primer, Test Fixtures: Using the Same Data Configuration for Multiple Tests
class EraseFileSystemSignaturesTest : public ::testing::Test
{
protected:
virtual void create_image_file(Byte_Value size);
virtual void write_intel_software_raid_signature();
virtual bool image_contains_all_zeros();
virtual void TearDown();
bool erase_filesystem_signatures(const Partition& partition, OperationDetail& operationdetail)
{ return m_gparted_core.erase_filesystem_signatures(partition, operationdetail); };
static const char* s_image_name;
GParted_Core m_gparted_core;
Partition m_partition;
OperationDetail m_operation_detail;
};
const char* EraseFileSystemSignaturesTest::s_image_name = "test_EraseFileSystemSignatures.img";
void EraseFileSystemSignaturesTest::create_image_file(Byte_Value size)
{
// Create new image file to work with.
unlink(s_image_name);
int fd = open(s_image_name, O_WRONLY|O_CREAT|O_NONBLOCK, 0666);
ASSERT_GE(fd, 0) << "Failed to create image file '" << s_image_name << "'. errno="
<< errno << "," << strerror(errno);
ASSERT_EQ(ftruncate(fd, (off_t)size), 0) << "Failed to set image file '" << s_image_name << "' to size "
<< size << ". errno=" << errno << "," << strerror(errno);
close(fd);
// Initialise m_partition as a Partition object spanning the whole of the image file.
m_partition.Reset();
PedDevice* lp_device = ped_device_get(s_image_name);
ASSERT_TRUE(lp_device != NULL);
m_partition.set_unpartitioned(s_image_name,
lp_device->path,
FS_UNALLOCATED,
lp_device->length,
lp_device->sector_size,
false);
ped_device_destroy(lp_device);
lp_device = NULL;
}
void EraseFileSystemSignaturesTest::write_intel_software_raid_signature()
{
int fd = open(s_image_name, O_WRONLY|O_NONBLOCK);
ASSERT_GE(fd, 0) << "Failed to open image file '" << s_image_name << "'. errno="
<< errno << "," << strerror(errno);
const char* signature = "Intel Raid ISM Cfg Sig. ";
size_t len_signature = strlen(signature);
// Write Intel Software RAID signature at -2 sectors before the end. Hard codes
// sector size to 512 bytes for a file.
// Reference:
// .../util-linux/libblkid/src/superblocks/isw_raid.c:probe_iswraid().
ASSERT_GE(lseek(fd, 2 * -512, SEEK_END), 0) << "Failed to seek in image file '" << s_image_name
<< "'. errno=" << errno << "," << strerror(errno);
ASSERT_EQ(write(fd, signature, len_signature), (ssize_t)len_signature)
<< "Failed to write to image file '" << s_image_name << "'. errno="
<< errno << "," << strerror(errno);
close(fd);
}
const char* first_non_zero_byte(const char* buf, size_t size)
{
while (size > 0)
{
if (*buf != '\0')
return buf;
buf++;
size--;
}
return NULL;
}
bool EraseFileSystemSignaturesTest::image_contains_all_zeros()
{
int fd = open(s_image_name, O_RDONLY|O_NONBLOCK);
if (fd < 0)
{
ADD_FAILURE() << __func__ << "(): Failed to open image file '" << s_image_name << "'. errno="
<< errno << "," << strerror(errno);
return false;
}
ssize_t bytes_read = 0;
size_t offset = 0;
do
{
char buf[BUFSIZ];
bytes_read = read(fd, buf, sizeof(buf));
if (bytes_read < 0)
{
ADD_FAILURE() << __func__ << "(): Failed to read from image file '" << s_image_name
<< "'. errno=" << errno << "," << strerror(errno);
close(fd);
return false;
}
const char* p = first_non_zero_byte(buf, bytes_read);
if (p != NULL)
{
ADD_FAILURE() << __func__ << "(): First non-zero bytes:\n"
<< binary_string_to_print(offset + (p - buf), p, buf + bytes_read - p);
close(fd);
return false;
}
}
while (bytes_read > 0);
close(fd);
return true;
}
void EraseFileSystemSignaturesTest::TearDown()
{
unlink(s_image_name);
}
TEST_F(EraseFileSystemSignaturesTest, IntelSoftwareRAIDAligned)
{
create_image_file(16 * MEBIBYTE);
write_intel_software_raid_signature();
EXPECT_TRUE(erase_filesystem_signatures(m_partition, m_operation_detail)) << m_operation_detail;
EXPECT_TRUE(image_contains_all_zeros());
}
TEST_F(EraseFileSystemSignaturesTest, IntelSoftwareRAIDUnaligned)
{
create_image_file(16 * MEBIBYTE - 512);
write_intel_software_raid_signature();
EXPECT_TRUE(erase_filesystem_signatures(m_partition, m_operation_detail)) << m_operation_detail;
EXPECT_TRUE(image_contains_all_zeros());
}
} // namespace GParted
// Custom Google Test main().
// Reference:
// * Google Test, Primer, Writing the main() function
// https://github.com/google/googletest/blob/master/googletest/docs/primer.md#writing-the-main-function
int main(int argc, char** argv)
{
printf("Running main() from %s\n", __FILE__);
Move duplicated test code into shared modules (#220) Move common testing code which doesn't need linking with GParted objects into the common module. Move the remaining common code used to print GParted objects using the insertion operator (operator<<) into the insertion_operators module. Split the common code like this so that the operator<<(std::ostream&, const OperationDetail&) function is not included in test_PipeCapture and it is not forced to link with all the non-UI related GParted objects. The Automake manual provides guidance that when a header belongs to a single program it is recommended to be listed in the program's _SOURCES variable and for a directory only containing header files listing them in the noinst_HEADERS variable is the right variable to use [1]. However the guidance doesn't cover this case for common.h and insertion_operators.h; header files in a directory with other files and used by multiple programs. So just because we have gparted_core_OBJECTS (normal Makefile, not Automake special variable) listing objects to link with, choose to use noinst_HEADERS Automake variable to list needed headers. [1] GNU Automake manual, 9.2 Header files https://www.gnu.org/software/automake/manual/html_node/Headers.html "Usually, only header files that accompany installed libraries need to be installed. Headers used by programs or convenience libraries are not installed. The noinst_HEADERS variable can be used for such headers. However, when the header belongs to a single convenience library or program, we recommend listing it in the program's or library's _SOURCES variable (see Defining program sources) instead of in noinst_HEADERS. This is clearer for the Makefile.am reader. noinst_HEADERS would be the right variable to use in a directory containing only headers and no associated library or program. All header files must be listed somewhere; in a _SOURCES variable or in a _HEADERS variable. Missing ones will not appear in the distribution. " Closes #220 - Format to Cleared not clearing "pdc" ataraid signature
2023-02-05 04:16:43 -07:00
GParted::ensure_x11_display(argc, argv);
Add initial unit test of erase_filesystem_signatures() (#220) Initially just testing erasing of Intel Software RAID signatures. Chosen because it was expected to work, but turned out not to be true in all cases. The code needs to initialise GParted_Core::mainthread, construct Gtk::Main() and execute xvfb-run because of this call chain: GParted_Core::erase_filesystem_signatures() GParted_Core::settle_device() Utils::execute_command ("udevadm settle ...") status.foreground = (Glib::Thread::self() == GParted_Core::mainthread) Gtk::Main::run() This was also needed when testing file system interface classes as discussed in commits [1][2]. The test fails like this: $ ./test_EraseFileSystemSignatures ... [ RUN ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDAligned [ OK ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDAligned (155 ms) [ RUN ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDUnaligned test_EraseFileSystemSignatures.cc:286: Failure Failed image_contains_all_zeros(): First non-zero bytes: 0x00001A00 "Intel Raid ISM C" 49 6E 74 65 6C 20 52 61 69 64 20 49 53 4D 20 43 test_EraseFileSystemSignatures.cc:320: Failure Value of: image_contains_all_zeros() Actual: false Expected: true [ FAILED ] EraseFileSystemSignaturesTest.IntelSoftwareRAIDUnaligned (92 ms) Manually write the same test image: $ python << 'EOF' signature = b'Intel Raid ISM Cfg Sig. ' import os fd = os.open('/tmp/test.img', os.O_CREAT|os.O_WRONLY) os.ftruncate(fd, 16*1024*1024 - 512) os.lseek(fd, -(2*512), os.SEEK_END) os.write(fd, signature) os.close(fd) EOF Run gpartedbin /tmp/test.img and Format to > Cleared. GParted continues to display the the image file as containing an ataraid signature. $ blkid /tmp/test.img /tmp/test.img: TYPE="isw_raid_member" $ hexdump -C /tmp/test.img 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00fffa00 49 6e 74 65 6c 20 52 61 69 64 20 49 53 4d 20 43 |Intel Raid ISM C| 00fffa10 66 67 20 53 69 67 2e 20 00 00 00 00 00 00 00 00 |fg Sig. ........| 00fffa20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| * 00fffe00 This signature is not being cleared when the device/partition/image size is 512 bytes smaller than a whole MiB because the last 3.5 KiB is left unwritten. This is because the last block of zeros written is 8 KiB aligned to 4 KiB at the end of the device. [1] a97c23c57c693b77724970d2f99702d7be18b4bc Add initial create ext2 only FileSystem interface class test (!49) [2] 8db9a83b39b82785dcbcc776ac259aa7986c0955 Run test program under xvfb-run to satisfy need for an X11 display (!49) Closes #220 - Format to Cleared not clearing "pdc" ataraid signature
2023-02-04 08:36:37 -07:00
// Initialise threading in GParted to successfully use Utils:: and
// FileSystem::execute_command(). Must be before InitGoogleTest().
GParted::GParted_Core::mainthread = Glib::Thread::self();
Gtk::Main gtk_main = Gtk::Main();
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}