From 0c85637a06587ef2409d3e8891a48c77dd68a91f Mon Sep 17 00:00:00 2001 From: Marcus Holland-Moritz Date: Tue, 27 Jun 2023 18:40:00 +0200 Subject: [PATCH] Make dwarfs_tools test work on Windows --- CMakeLists.txt | 4 +- test/dwarfs_tools.cpp | 116 ++++++++++++++++++++++++++++++------------ 2 files changed, 85 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 40d195c5..5ef86c66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -616,9 +616,7 @@ if(WITH_TESTS) gtest_discover_tests(dwarfs_test) gtest_discover_tests(dwarfs_compat_test) gtest_discover_tests(dwarfs_badfs_test) - if(NOT WIN32) - gtest_discover_tests(dwarfs_tools_test) - endif() + gtest_discover_tests(dwarfs_tools_test) target_compile_definitions(dwarfs_compat_test PRIVATE TEST_DATA_DIR=\"${CMAKE_SOURCE_DIR}/test\") diff --git a/test/dwarfs_tools.cpp b/test/dwarfs_tools.cpp index c7bff5ff..b3f9acff 100644 --- a/test/dwarfs_tools.cpp +++ b/test/dwarfs_tools.cpp @@ -49,6 +49,8 @@ #include +#include "dwarfs/file_stat.h" + #include "test_helpers.h" namespace { @@ -56,17 +58,23 @@ namespace { namespace bp = boost::process; namespace fs = std::filesystem; -auto data_archive = fs::path(TEST_DATA_DIR) / "data.tar"; +auto data_archive = fs::path(TEST_DATA_DIR).make_preferred() / "data.tar"; -auto tools_dir = fs::path(TOOLS_BIN_DIR); -auto mkdwarfs_bin = tools_dir / "mkdwarfs"; -auto fuse3_bin = tools_dir / "dwarfs"; -auto fuse2_bin = tools_dir / "dwarfs2"; -auto dwarfsextract_bin = tools_dir / "dwarfsextract"; -auto dwarfsck_bin = tools_dir / "dwarfsck"; +#ifdef _WIN32 +#define EXE_EXT ".exe" +#else +#define EXE_EXT "" +#endif -auto diff_bin = fs::path(DIFF_BIN); -auto tar_bin = fs::path(TAR_BIN); +auto tools_dir = fs::path(TOOLS_BIN_DIR).make_preferred(); +auto mkdwarfs_bin = tools_dir / "mkdwarfs" EXE_EXT; +auto fuse3_bin = tools_dir / "dwarfs" EXE_EXT; +auto fuse2_bin = tools_dir / "dwarfs2" EXE_EXT; +auto dwarfsextract_bin = tools_dir / "dwarfsextract" EXE_EXT; +auto dwarfsck_bin = tools_dir / "dwarfsck" EXE_EXT; + +auto diff_bin = fs::path(DIFF_BIN).make_preferred(); +auto tar_bin = fs::path(TAR_BIN).make_preferred(); #ifndef _WIN32 pid_t get_dwarfs_pid(fs::path const& path) { @@ -80,19 +88,49 @@ pid_t get_dwarfs_pid(fs::path const& path) { } #endif +bool wait_until_file_ready(fs::path const& path, + std::chrono::milliseconds timeout) { + auto end = std::chrono::steady_clock::now() + timeout; + std::error_code ec; + while (!fs::exists(path, ec)) { + if (ec) { + std::cerr << "*** exists: " << ec.message() << "\n"; + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + if (std::chrono::steady_clock::now() >= end) { + return false; + } + } + return true; +} + +#ifdef _WIN32 +struct new_process_group : public ::boost::process::detail::handler_base { + template + void on_setup(WindowsExecutor& e [[maybe_unused]]) const { + e.creation_flags |= CREATE_NEW_PROCESS_GROUP; + } +}; +#endif + class subprocess { public: template - subprocess(Args&&... args) { + subprocess(std::filesystem::path const& prog, Args&&... args) { std::vector cmdline; (append_arg(cmdline, std::forward(args)), ...); try { - c_ = bp::child(bp::args(cmdline), bp::std_in.close(), bp::std_out > out_, - bp::std_err > err_, ios_); + c_ = bp::child(prog.string(), bp::args(cmdline), bp::std_in.close(), + bp::std_out > out_, bp::std_err > err_, ios_ +#ifdef _WIN32 + , + new_process_group() +#endif + ); } catch (...) { - std::cerr << "failed to create subprocess: " << folly::join(' ', cmdline) - << "\n"; + std::cerr << "failed to create subprocess: " << prog << " " + << folly::join(' ', cmdline) << "\n"; throw; } } @@ -233,6 +271,8 @@ class driver_runner { process_ = std::make_unique(driver, image, mountpoint, std::forward(args)...); process_->run_background(); + + wait_until_file_ready(mountpoint, std::chrono::seconds(5)); #else if (!subprocess::check_run(driver, image, mountpoint, std::forward(args)...)) { @@ -269,7 +309,7 @@ class driver_runner { constexpr int expected_exit_code = SIGINT; #endif process_->interrupt(); - process_->wait(); // TODO: wait_for? + process_->wait(); auto ec = process_->exit_code(); if (ec != expected_exit_code) { std::cerr << "driver failed to unmount:\nout:\n" @@ -328,18 +368,6 @@ class driver_runner { #endif }; -bool wait_until_file_ready(fs::path const& path, - std::chrono::milliseconds timeout) { - auto end = std::chrono::steady_clock::now() + timeout; - while (!fs::exists(path)) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - if (std::chrono::steady_clock::now() >= end) { - return false; - } - } - return true; -} - bool check_readonly(fs::path const& p, bool readonly = false) { auto st = fs::status(p); bool is_writable = @@ -351,16 +379,25 @@ bool check_readonly(fs::path const& p, bool readonly = false) { return false; } +#ifndef _WIN32 if (::access(p.string().c_str(), W_OK) == 0) { // access(W_OK) should never succeed ::perror("access"); return false; } +#endif return true; } -size_t num_hardlinks(fs::path const& p) { return fs::hard_link_count(p); } +size_t num_hardlinks(fs::path const& p) { +#ifdef _WIN32 + auto stat = dwarfs::make_file_stat(p); + return stat.nlink; +#else + return fs::hard_link_count(p); +#endif +} } // namespace @@ -399,8 +436,14 @@ TEST(tools, everything) { } std::vector all_options{ - "-s", "-oenable_nlink", "-oreadonly", - "-omlock=try", "-ono_cache_image", "-ocache_files", + "-s", +#ifndef _WIN32 + "-oenable_nlink", + "-oreadonly", +#endif + "-omlock=try", + "-ono_cache_image", + "-ocache_files", }; for (auto const& driver : drivers) { @@ -446,11 +489,15 @@ TEST(tools, everything) { { driver_runner runner(driver, image, mountpoint, args); - ASSERT_TRUE( + EXPECT_TRUE( subprocess::check_run(diff_bin, "-qruN", data_dir, mountpoint)); +#ifndef _WIN32 + // TODO: `tar` on Windows doesn't preserve hardlinks -> use dwarfsextract EXPECT_EQ(enable_nlink ? 3 : 1, num_hardlinks(mountpoint / "format.sh")); + // This doesn't really work on Windows (yet) EXPECT_TRUE(check_readonly(mountpoint / "format.sh", readonly)); +#endif } args.push_back("-ooffset=auto"); @@ -458,11 +505,15 @@ TEST(tools, everything) { { driver_runner runner(driver, image_hdr, mountpoint, args); - ASSERT_TRUE( + EXPECT_TRUE( subprocess::check_run(diff_bin, "-qruN", data_dir, mountpoint)); +#ifndef _WIN32 + // TODO: `tar` on Windows doesn't preserve hardlinks -> use dwarfsextract EXPECT_EQ(enable_nlink ? 3 : 1, num_hardlinks(mountpoint / "format.sh")); + // This doesn't really work on Windows (yet) EXPECT_TRUE(check_readonly(mountpoint / "format.sh", readonly)); +#endif } } } @@ -476,6 +527,7 @@ TEST(tools, everything) { { std::string header; + EXPECT_TRUE(folly::readFile(header_data.string().c_str(), header)); auto output = subprocess::check_run(dwarfsck_bin, image_hdr, "-H");