kernel: Use fs:: namespace and unicode path in kernel tests
What changed, and why it matters
This commit updates Bitcoin Core's kernel test suite to use the project's own filesystem helpers and to include a Windows application manifest. It also makes the temporary test directories contain a corn emoji (🌽) to exercise Unicode path handling. The changes are test-only and defensive: they improve test coverage for non-ASCII paths on Windows, but do not fix a live vulnerability in production code.
No security response required. Treat as normal test-quality improvement. Reviewers may verify that the fs:: wrappers correctly propagate UTF-8 paths through the kernel API on Windows CI.
Security signals we found
Test-only hardening for Unicode path handling on Windows
Adds Windows application manifest to test_kernel.exe for UTF-8 codepage support
Replaces raw std::filesystem with project fs:: wrappers that normalize UTF-8 path handling
No changes to consensus, networking, wallet, or P2P code
Evidence from the diff
The patch removes direct std::filesystem usage from src/test/kernel/test_kernel.cpp in favor of Bitcoin Core’s fs:: wrappers (fs::path, fs::create_directories, fs::remove_all, fs::exists, fs::u8path, PathToString). It adds add_windows_application_manifest(test_kernel) so the test binary declares UTF-8 codepage support on Windows, and removes test_kernel.exe from CI manifest-skipped lists. A lint exclusion for the kernel test file is also removed. The test directory name now includes a Unicode corn emoji to verify the kernel API handles UTF-8 paths. No production kernel or node code is changed.
Changed components
src/test/kernel/test_kernel.cppsrc/test/kernel/CMakeLists.txt.github/ci-windows-cross.py.github/ci-windows.pytest/lint/test_runner/src/lint_cpp.rsInspect captured patch +18 / −19
diff --git a/.github/ci-windows-cross.py b/.github/ci-windows-cross.py
index 90cd59c7..fd161890 100755
--- a/.github/ci-windows-cross.py
+++ b/.github/ci-windows-cross.py
@@ -41,7 +41,6 @@ def check_manifests():
skipped = { # Skip as they currently do not have manifests
"fuzz.exe",
"bench_bitcoin.exe",
- "test_kernel.exe",
}
for entry in release_dir.iterdir():
if entry.suffix.lower() != ".exe":
diff --git a/.github/ci-windows.py b/.github/ci-windows.py
index 16d7db7a..a6f27b69 100755
--- a/.github/ci-windows.py
+++ b/.github/ci-windows.py
@@ -106,7 +106,6 @@ def check_manifests(ci_type):
"fuzz.exe",
"bench_bitcoin.exe",
"test_bitcoin-qt.exe",
- "test_kernel.exe",
"bitcoin-chainstate.exe",
}
for entry in release_dir.iterdir():
diff --git a/src/test/kernel/CMakeLists.txt b/src/test/kernel/CMakeLists.txt
index d2d42dc8..b1fc6c73 100644
--- a/src/test/kernel/CMakeLists.txt
+++ b/src/test/kernel/CMakeLists.txt
@@ -9,4 +9,6 @@ target_link_libraries(test_kernel
Boost::headers
)
+add_windows_application_manifest(test_kernel)
+
add_test(NAME test_kernel COMMAND test_kernel)
diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp
index 5a380065..4a97f5ba 100644
--- a/src/test/kernel/test_kernel.cpp
+++ b/src/test/kernel/test_kernel.cpp
@@ -4,6 +4,7 @@
#include <kernel/bitcoinkernel.h>
#include <kernel/bitcoinkernel_wrapper.h>
+#include <util/fs.h>
#define BOOST_TEST_MODULE Bitcoin Kernel Test Suite
#include <boost/test/included/unit_test.hpp>
@@ -13,7 +14,6 @@
#include <charconv>
#include <cstdint>
#include <cstdlib>
-#include <filesystem>
#include <iostream>
#include <memory>
#include <optional>
@@ -98,16 +98,16 @@ public:
};
struct TestDirectory {
- std::filesystem::path m_directory;
+ fs::path m_directory;
TestDirectory(std::string directory_name)
- : m_directory{std::filesystem::temp_directory_path() / (directory_name + random_string(16))}
+ : m_directory{fs::path{fs::temp_directory_path()} / fs::u8path(directory_name + "_🌽_" + random_string(16))}
{
- std::filesystem::create_directories(m_directory);
+ fs::create_directories(m_directory);
}
~TestDirectory()
{
- std::filesystem::remove_all(m_directory);
+ fs::remove_all(m_directory);
}
};
@@ -725,19 +725,19 @@ BOOST_AUTO_TEST_CASE(btck_chainman_tests)
{ // test with default context
Context context{};
- ChainstateManagerOptions chainman_opts{context, test_directory.m_directory.string(), (test_directory.m_directory / "blocks").string()};
+ ChainstateManagerOptions chainman_opts{context, PathToString(test_directory.m_directory), PathToString(test_directory.m_directory / "blocks")};
ChainMan chainman{context, chainman_opts};
}
{ // test with default context options
ContextOptions options{};
Context context{options};
- ChainstateManagerOptions chainman_opts{context, test_directory.m_directory.string(), (test_directory.m_directory / "blocks").string()};
+ ChainstateManagerOptions chainman_opts{context, PathToString(test_directory.m_directory), PathToString(test_directory.m_directory / "blocks")};
ChainMan chainman{context, chainman_opts};
}
{ // null or empty data_directory or blocks_directory are not allowed
Context context{};
- auto valid_dir{test_directory.m_directory.string()};
+ auto valid_dir{PathToString(test_directory.m_directory)};
std::vector<std::pair<std::string_view, std::string_view>> illegal_cases{
{"", valid_dir},
{valid_dir, {nullptr, 0}},
@@ -753,7 +753,7 @@ BOOST_AUTO_TEST_CASE(btck_chainman_tests)
auto notifications{std::make_shared<TestKernelNotifications>()};
auto context{create_context(notifications, ChainType::MAINNET)};
- ChainstateManagerOptions chainman_opts{context, test_directory.m_directory.string(), (test_directory.m_directory / "blocks").string()};
+ ChainstateManagerOptions chainman_opts{context, PathToString(test_directory.m_directory), PathToString(test_directory.m_directory / "blocks")};
chainman_opts.SetWorkerThreads(4);
BOOST_CHECK(!chainman_opts.SetWipeDbs(/*wipe_block_tree=*/true, /*wipe_chainstate=*/false));
BOOST_CHECK(chainman_opts.SetWipeDbs(/*wipe_block_tree=*/true, /*wipe_chainstate=*/true));
@@ -769,7 +769,7 @@ std::unique_ptr<ChainMan> create_chainman(TestDirectory& test_directory,
bool chainstate_db_in_memory,
Context& context)
{
- ChainstateManagerOptions chainman_opts{context, test_directory.m_directory.string(), (test_directory.m_directory / "blocks").string()};
+ ChainstateManagerOptions chainman_opts{context, PathToString(test_directory.m_directory), PathToString(test_directory.m_directory / "blocks")};
if (reindex) {
chainman_opts.SetWipeDbs(/*wipe_block_tree=*/reindex, /*wipe_chainstate=*/reindex);
@@ -842,7 +842,7 @@ void chainman_reindex_chainstate_test(TestDirectory& test_directory)
/*block_tree_db_in_memory=*/false, /*chainstate_db_in_memory=*/false, context)};
std::vector<std::string> import_files;
- import_files.push_back((test_directory.m_directory / "blocks" / "blk00000.dat").string());
+ import_files.push_back(PathToString(test_directory.m_directory / "blocks" / "blk00000.dat"));
BOOST_CHECK(chainman->ImportBlocks(import_files));
}
@@ -992,9 +992,9 @@ BOOST_AUTO_TEST_CASE(btck_chainman_in_memory_tests)
BOOST_CHECK(new_block);
}
- BOOST_CHECK(std::filesystem::exists(in_memory_test_directory.m_directory / "blocks"));
- BOOST_CHECK(!std::filesystem::exists(in_memory_test_directory.m_directory / "blocks" / "index"));
- BOOST_CHECK(!std::filesystem::exists(in_memory_test_directory.m_directory / "chainstate"));
+ BOOST_CHECK(fs::exists(in_memory_test_directory.m_directory / "blocks"));
+ BOOST_CHECK(!fs::exists(in_memory_test_directory.m_directory / "blocks" / "index"));
+ BOOST_CHECK(!fs::exists(in_memory_test_directory.m_directory / "chainstate"));
BOOST_CHECK(context.interrupt());
}
@@ -1172,8 +1172,8 @@ BOOST_AUTO_TEST_CASE(btck_chainman_regtest_tests)
BOOST_CHECK_EQUAL(count, chain.CountEntries());
- std::filesystem::remove_all(test_directory.m_directory / "blocks" / "blk00000.dat");
+ fs::remove_all(test_directory.m_directory / "blocks" / "blk00000.dat");
BOOST_CHECK(!chainman->ReadBlock(tip_2).has_value());
- std::filesystem::remove_all(test_directory.m_directory / "blocks" / "rev00000.dat");
+ fs::remove_all(test_directory.m_directory / "blocks" / "rev00000.dat");
BOOST_CHECK_THROW(chainman->ReadBlockSpentOutputs(tip), std::runtime_error);
}
diff --git a/test/lint/test_runner/src/lint_cpp.rs b/test/lint/test_runner/src/lint_cpp.rs
index 250ebb46..4ad3c06d 100644
--- a/test/lint/test_runner/src/lint_cpp.rs
+++ b/test/lint/test_runner/src/lint_cpp.rs
@@ -105,7 +105,6 @@ pub fn lint_std_filesystem() -> LintResult {
"./src/",
":(exclude)src/ipc/libmultiprocess/",
":(exclude)src/util/fs.h",
- ":(exclude)src/test/kernel/test_kernel.cpp",
":(exclude)src/bitcoin-chainstate.cpp",
])
.status()
Why this scored 19/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.