nit: in unsupported environments, the assertions never run, but the tests still pass. This can create false-positive test coverage.
<details>
<summary>diff</summary>
diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp
index 79f5b15177..9cd6c2d61c 100644
--- a/src/test/blockmanager_tests.cpp
+++ b/src/test/blockmanager_tests.cpp
@@ -331,7 +331,7 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_io_failure_consistency, TestChain100Setup)
auto& blockman = m_node.chainman->m_blockman;
CBlockIndex& tip{*WITH_LOCK(chainman->GetMutex(), return chainman->ActiveTip())};
- SimulateFileSystemError(m_path_root, m_args.GetBlocksDirPath().parent_path(), [&]() {
+ if (!SimulateFileSystemError(m_path_root, m_args.GetBlocksDirPath().parent_path(), [&]() {
{
ASSERT_DEBUG_LOG("OpenBlockFile failed");
FlatFilePos tip_pos = WITH_LOCK(chainman->GetMutex(), return tip.GetBlockPos());
@@ -372,7 +372,10 @@ BOOST_FIXTURE_TEST_CASE(blockmanager_io_failure_consistency, TestChain100Setup)
tip.nStatus |= BLOCK_HAVE_UNDO;
}
- });
+ })) {
+ BOOST_WARN_MESSAGE(false, "skipping: unable to enforce filesystem error simulation");
+ return;
+ }
// Ensure we haven't corrupted any internal state during the failure.
// Check we can read the block again now that there is no dir access issue going on.
diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp
index e33c12265b..f835c5e8cb 100644
--- a/src/test/flatfile_tests.cpp
+++ b/src/test/flatfile_tests.cpp
@@ -164,11 +164,14 @@ BOOST_AUTO_TEST_CASE(flatfile_open_write_missing_unwritable_parent)
fs::create_directories(dir);
// Make it read-only so creating subdirectories fails.
- SimulateFileSystemError(m_path_root, dir, [&dir]() {
+ if (!SimulateFileSystemError(m_path_root, dir, [&dir]() {
const FlatFileSeq seq(dir / "blocks", "a", DUMMY_CHUNK_SZ);
const AutoFile file{seq.Open(FlatFilePos(0, 0), /*read_only=*/false)};
BOOST_CHECK(file.IsNull());
- });
+ })) {
+ BOOST_WARN_MESSAGE(false, "skipping: unable to enforce filesystem error simulation");
+ return;
+ }
}
BOOST_AUTO_TEST_CASE(flatfile_flush_unwritable_dir)
@@ -178,9 +181,12 @@ BOOST_AUTO_TEST_CASE(flatfile_flush_unwritable_dir)
fs::create_directories(data_dir);
const FlatFileSeq seq(data_dir, "a", DUMMY_CHUNK_SZ);
- SimulateFileSystemError(m_path_root, data_dir, [&seq]() {
+ if (!SimulateFileSystemError(m_path_root, data_dir, [&seq]() {
BOOST_CHECK(!seq.Flush(FlatFilePos(0, 1)));
- });
+ })) {
+ BOOST_WARN_MESSAGE(false, "skipping: unable to enforce filesystem error simulation");
+ return;
+ }
}
BOOST_AUTO_TEST_CASE(flatfile_allocate_unwritable_dir)
@@ -192,12 +198,15 @@ BOOST_AUTO_TEST_CASE(flatfile_allocate_unwritable_dir)
fs::create_directories(data_dir);
const FlatFileSeq seq(data_dir, "a", DUMMY_CHUNK_SZ);
- SimulateFileSystemError(m_path_root, data_dir, [&seq]() {
+ if (!SimulateFileSystemError(m_path_root, data_dir, [&seq]() {
bool out_of_space;
// Note: this throws due to 'CheckDiskSpace'. In the future, this shouldn't throw either.
BOOST_CHECK_THROW(seq.Allocate(FlatFilePos(0, 0), 1, out_of_space), fs::filesystem_error);
BOOST_CHECK(!out_of_space);
- });
+ })) {
+ BOOST_WARN_MESSAGE(false, "skipping: unable to enforce filesystem error simulation");
+ return;
+ }
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util/common.cpp b/src/test/util/common.cpp
index e89fe11e71..43f666e5fb 100644
--- a/src/test/util/common.cpp
+++ b/src/test/util/common.cpp
@@ -4,19 +4,25 @@
#include <test/util/common.h>
-void SimulateFileSystemError(const fs::path& test_root_dir, const fs::path& path, const std::function<void()>& fn)
+#include <stdexcept>
+
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
+[[nodiscard]] bool SimulateFileSystemError(const fs::path& test_root_dir, const fs::path& path, const std::function<void()>& fn)
{
#ifdef WIN32
// On Windows, any open file (such as the db) prevents directory renaming,
// so we can't simulate a filesystem error in this platform. Skip it.
- return;
+ return false;
#else
// This check relies on filesystem permission manipulation to simulate I/O
// failures. Running as root bypasses permission checks, so the OS will
// allow directory creation and file writes even when perms are set to
// none, making it impossible to simulate the expected failures.
- if (getuid() == 0) return;
- #endif
+ if (geteuid() == 0) return false;
+#endif
const fs::path root = fs::weakly_canonical(test_root_dir);
const fs::path target = fs::weakly_canonical(path);
@@ -55,4 +61,5 @@ void SimulateFileSystemError(const fs::path& test_root_dir, const fs::path& path
}
restore();
+ return true;
}
diff --git a/src/test/util/common.h b/src/test/util/common.h
index 77df87cff0..457c33cb7f 100644
--- a/src/test/util/common.h
+++ b/src/test/util/common.h
@@ -8,14 +8,11 @@
#include <util/fs.h>
#include <chrono>
+#include <functional>
#include <optional>
#include <ostream>
#include <string>
-#ifndef WIN32
-#include <unistd.h>
-#endif
-
/**
* BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
* Use as
@@ -70,8 +67,8 @@ inline std::ostream& operator<<(std::ostream& os, const T& obj)
* unavailable. The target is removed and its parent made inaccessible while
* `fn()` executes, then everything is restored.
*
- * Note: Returns early if the environment does not allow simulating a fs error.
+ * Returns false if the environment does not allow simulating a fs error.
*/
-void SimulateFileSystemError(const fs::path& test_root_dir, const fs::path& path, const std::function<void()>& fn);
+[[nodiscard]] bool SimulateFileSystemError(const fs::path& test_root_dir, const fs::path& path, const std::function<void()>& fn);
#endif // BITCOIN_TEST_UTIL_COMMON_H
diff --git a/src/test/validation_chainstate_tests.cpp b/src/test/validation_chainstate_tests.cpp
index 1b8cf7121e..7df38707c6 100644
--- a/src/test/validation_chainstate_tests.cpp
+++ b/src/test/validation_chainstate_tests.cpp
@@ -201,15 +201,21 @@ BOOST_FIXTURE_TEST_CASE(activate_best_chain_connect_io_failure, TestChain100Setu
// Inaccessible block file must trigger a fatal error.
const auto blk_file = blocks_dir / "blk00000.dat";
- SimulateFileSystemError(m_path_root, blk_file, [&]() {
+ if (!SimulateFileSystemError(m_path_root, blk_file, [&]() {
check_activate_best_chain_fatal_error("Failed to read block", m_node, chainstate);
- });
+ })) {
+ BOOST_WARN_MESSAGE(false, "skipping: unable to enforce filesystem error simulation");
+ return;
+ }
// Inaccessible blocks directory must also trigger a fatal error.
const auto parent_dir = blocks_dir.parent_path();
- SimulateFileSystemError(m_path_root, parent_dir, [&]() {
+ if (!SimulateFileSystemError(m_path_root, parent_dir, [&]() {
check_activate_best_chain_fatal_error("Failed to read block", m_node, chainstate);
- });
+ })) {
+ BOOST_WARN_MESSAGE(false, "skipping: unable to enforce filesystem error simulation");
+ return;
+ }
// Sanity check: the block reconnects once the filesystem is restored.
BlockValidationState state;
@@ -255,21 +261,30 @@ BOOST_FIXTURE_TEST_CASE(activate_best_chain_disconnect_io_failure, TestChain100S
// Inaccessible block file during reorg must trigger fatal error.
const auto blk_file = blocks_dir / "blk00000.dat";
- SimulateFileSystemError(m_path_root, blk_file, [&]() {
+ if (!SimulateFileSystemError(m_path_root, blk_file, [&]() {
check_activate_best_chain_fatal_error("Failed to disconnect block", m_node, chainstate);
- });
+ })) {
+ BOOST_WARN_MESSAGE(false, "skipping: unable to enforce filesystem error simulation");
+ return;
+ }
// Inaccessible undo file during reorg must trigger fatal error.
const auto rev_file = blocks_dir / "rev00000.dat";
- SimulateFileSystemError(m_path_root, rev_file, [&]() {
+ if (!SimulateFileSystemError(m_path_root, rev_file, [&]() {
check_activate_best_chain_fatal_error("Failed to disconnect block", m_node, chainstate);
- });
+ })) {
+ BOOST_WARN_MESSAGE(false, "skipping: unable to enforce filesystem error simulation");
+ return;
+ }
// Inaccessible blocks directory during reorg must also trigger fatal error.
const auto parent_dir = blocks_dir.parent_path();
- SimulateFileSystemError(m_path_root, parent_dir, [&]() {
+ if (!SimulateFileSystemError(m_path_root, parent_dir, [&]() {
check_activate_best_chain_fatal_error("Failed to disconnect block", m_node, chainstate);
- });
+ })) {
+ BOOST_WARN_MESSAGE(false, "skipping: unable to enforce filesystem error simulation");
+ return;
+ }
// Sanity check: the reorg completes once the filesystem is restored.
state = BlockValidationState();
</details>