diff options
author | Yuqian Yang <crupest@crupest.life> | 2025-09-02 22:29:11 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2025-09-03 01:52:38 +0800 |
commit | 545a638929218a83d194402b3d52f5bffd87d9eb (patch) | |
tree | f659f671fec17f3ef7dced31f3a1f59673d18690 | |
parent | 5035f18f44f675af2faa4019b6de14b3f3aab270 (diff) | |
download | cru-545a638929218a83d194402b3d52f5bffd87d9eb.tar.gz cru-545a638929218a83d194402b3d52f5bffd87d9eb.tar.bz2 cru-545a638929218a83d194402b3d52f5bffd87d9eb.zip |
UnixFileDescriptor.
-rw-r--r-- | include/cru/base/platform/unix/PosixSpawnSubProcess.h | 6 | ||||
-rw-r--r-- | include/cru/base/platform/unix/UnixFile.h | 61 | ||||
-rw-r--r-- | include/cru/base/platform/unix/UnixFileStream.h | 8 | ||||
-rw-r--r-- | include/cru/base/platform/unix/UnixPipe.h | 68 | ||||
-rw-r--r-- | src/base/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/base/platform/unix/PosixSpawnSubProcess.cpp | 119 | ||||
-rw-r--r-- | src/base/platform/unix/UnixFile.cpp | 96 | ||||
-rw-r--r-- | src/base/platform/unix/UnixFileStream.cpp | 20 | ||||
-rw-r--r-- | src/base/platform/unix/UnixPipe.cpp | 51 | ||||
-rw-r--r-- | test/common/CMakeLists.txt | 1 | ||||
-rw-r--r-- | test/common/platform/unix/UnixFileTest.cpp | 38 |
11 files changed, 257 insertions, 213 deletions
diff --git a/include/cru/base/platform/unix/PosixSpawnSubProcess.h b/include/cru/base/platform/unix/PosixSpawnSubProcess.h index 5f2200f5..c691984b 100644 --- a/include/cru/base/platform/unix/PosixSpawnSubProcess.h +++ b/include/cru/base/platform/unix/PosixSpawnSubProcess.h @@ -9,7 +9,7 @@ #include "../../io/AutoReadStream.h" #include "UnixFileStream.h" -#include "UnixPipe.h" +#include "UnixFile.h" #include <spawn.h> @@ -34,10 +34,6 @@ class PosixSpawnSubProcessImpl : public Object, pid_t pid_; int exit_code_; - UnixPipe stdin_pipe_; - UnixPipe stdout_pipe_; - UnixPipe stderr_pipe_; - std::unique_ptr<UnixFileStream> stdin_stream_; std::unique_ptr<UnixFileStream> stdout_stream_; std::unique_ptr<UnixFileStream> stderr_stream_; diff --git a/include/cru/base/platform/unix/UnixFile.h b/include/cru/base/platform/unix/UnixFile.h new file mode 100644 index 00000000..63545428 --- /dev/null +++ b/include/cru/base/platform/unix/UnixFile.h @@ -0,0 +1,61 @@ +#pragma once + +#if !defined(__unix) +#error "This file can only be included on unix." +#endif + +#include "../../Bitmask.h" + +#include <functional> + +namespace cru::platform::unix { +class UnixFileDescriptor { + public: + UnixFileDescriptor(); + /** + * \param close Default to POSIX close function. + */ + explicit UnixFileDescriptor(int descriptor, bool auto_close = true, + std::function<int(int)> close = {}); + ~UnixFileDescriptor(); + + UnixFileDescriptor(const UnixFileDescriptor& other) = delete; + UnixFileDescriptor& operator=(const UnixFileDescriptor& other) = delete; + + UnixFileDescriptor(UnixFileDescriptor&& other) noexcept; + UnixFileDescriptor& operator=(UnixFileDescriptor&& other) noexcept; + + bool IsValid() const; + int GetValue() const; + void Close(); + + bool IsAutoClose() const { return auto_close_; } + void SetAutoClose(bool auto_close) { this->auto_close_ = auto_close; } + + explicit operator bool() const { return this->IsValid(); } + operator int() const { return this->GetValue(); } + + private: + bool DoClose(); + + private: + int descriptor_; + bool auto_close_; + std::function<int(int)> close_; +}; + +struct UniDirectionalUnixPipeResult { + UnixFileDescriptor read; + UnixFileDescriptor write; +}; + +namespace details { +struct UnixPipeFlagTag; +} +using UnixPipeFlag = Bitmask<details::UnixPipeFlagTag>; +struct UnixPipeFlags { + constexpr static auto NonBlock = UnixPipeFlag::FromOffset(1); +}; + +UniDirectionalUnixPipeResult OpenUniDirectionalPipe(UnixPipeFlag flags = {}); +} // namespace cru::platform::unix diff --git a/include/cru/base/platform/unix/UnixFileStream.h b/include/cru/base/platform/unix/UnixFileStream.h index 0709a326..b97c22de 100644 --- a/include/cru/base/platform/unix/UnixFileStream.h +++ b/include/cru/base/platform/unix/UnixFileStream.h @@ -5,6 +5,7 @@ #endif #include "../../io/Stream.h" +#include "UnixFile.h" namespace cru::platform::unix { class UnixFileStream : public io::Stream { @@ -13,8 +14,8 @@ class UnixFileStream : public io::Stream { public: UnixFileStream(const char* path, int oflag, mode_t mode = 0660); - UnixFileStream(int fd, bool can_seek, bool can_read, bool can_write, - bool auto_close); + UnixFileStream(UnixFileDescriptor fd, bool can_seek, bool can_read, + bool can_write); ~UnixFileStream() override; public: @@ -31,7 +32,6 @@ class UnixFileStream : public io::Stream { void DoClose(); private: - int file_descriptor_; // -1 for no file descriptor - bool auto_close_; + UnixFileDescriptor file_descriptor_; }; } // namespace cru::platform::unix diff --git a/include/cru/base/platform/unix/UnixPipe.h b/include/cru/base/platform/unix/UnixPipe.h deleted file mode 100644 index 830ddf62..00000000 --- a/include/cru/base/platform/unix/UnixPipe.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#if !defined(__unix) -#error "This file can only be included on unix." -#endif - -#include "../../Base.h" -#include "../../Bitmask.h" - -namespace cru::platform::unix { -namespace details { -struct UnixPipeFlagTag; -} -using UnixPipeFlag = Bitmask<details::UnixPipeFlagTag>; -struct UnixPipeFlags { - constexpr static auto NonBlock = UnixPipeFlag::FromOffset(1); -}; - -/** - * @brief an unix pipe, commonly for communication of parent process and child - * process. - * - * There are two types of pipes sorted by its usage. For stdin, parent process - * SEND data to child process. For stdout and stderr, parent process RECEIVE - * data from child process. Each pipe has two ends, one for read and the other - * for write. But for send and receive, they are reversed. It is a little - * confused to consider which end should be used by parent and which end should - * be used by child. So this class help you make it clear. You specify SEND or - * RECEIVE, and this class give you a parent used end and a child used end. - * - * This class will only close the end used by parent when it is destructed. It - * is the user's duty to close the one used by child. - */ -class UnixPipe : public Object { - private: - constexpr static auto kLogTag = u"cru::platform::unix::UnixPipe"; - - public: - enum class Usage { - Send, - Receive, - }; - - explicit UnixPipe(Usage usage, bool auto_close, UnixPipeFlag flags = {}); - - CRU_DELETE_COPY(UnixPipe) - CRU_DELETE_MOVE(UnixPipe) - - ~UnixPipe(); - - /** - * @brief aka, the one used by parent process. - */ - int GetSelfFileDescriptor(); - - /** - * @brief aka, the one used by child process. - */ - int GetOtherFileDescriptor(); - - private: - Usage usage_; - bool auto_close_; - UnixPipeFlag flags_; - int read_fd_; - int write_fd_; -}; -} // namespace cru::platform::unix diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 6f33441d..e0a16768 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -25,8 +25,8 @@ target_compile_definitions(CruBase PUBLIC $<$<CONFIG:Debug>:CRU_DEBUG>) if (UNIX AND NOT EMSCRIPTEN) target_sources(CruBase PRIVATE platform/unix/PosixSpawnSubProcess.cpp + platform/unix/UnixFile.cpp platform/unix/UnixFileStream.cpp - platform/unix/UnixPipe.cpp ) if (NOT APPLE) diff --git a/src/base/platform/unix/PosixSpawnSubProcess.cpp b/src/base/platform/unix/PosixSpawnSubProcess.cpp index 631c14b8..25e6c38c 100644 --- a/src/base/platform/unix/PosixSpawnSubProcess.cpp +++ b/src/base/platform/unix/PosixSpawnSubProcess.cpp @@ -14,24 +14,7 @@ #include <unordered_map> namespace cru::platform::unix { -PosixSpawnSubProcessImpl::PosixSpawnSubProcessImpl() - : pid_(0), - exit_code_(0), - stdin_pipe_(UnixPipe::Usage::Send, false), - stdout_pipe_(UnixPipe::Usage::Receive, false), - stderr_pipe_(UnixPipe::Usage::Receive, false) { - stdin_stream_ = std::make_unique<UnixFileStream>( - stdin_pipe_.GetSelfFileDescriptor(), false, false, true, true); - stdout_stream_ = std::make_unique<UnixFileStream>( - stdout_pipe_.GetSelfFileDescriptor(), false, true, false, true); - stderr_stream_ = std::make_unique<UnixFileStream>( - stderr_pipe_.GetSelfFileDescriptor(), false, true, false, true); - - stdout_buffer_stream_ = - std::make_unique<io::AutoReadStream>(stdout_stream_.get(), true, false); - stderr_buffer_stream_ = - std::make_unique<io::AutoReadStream>(stderr_stream_.get(), true, false); -} +PosixSpawnSubProcessImpl::PosixSpawnSubProcessImpl() : pid_(0), exit_code_(0) {} PosixSpawnSubProcessImpl::~PosixSpawnSubProcessImpl() {} @@ -71,8 +54,7 @@ void DestroyCstrArray(char** argv) { void PosixSpawnSubProcessImpl::PlatformCreateProcess( const SubProcessStartInfo& start_info) { - int error; - auto check_error = [&error](String message) { + auto check_error = [](int error, String message) { if (error == 0) return; std::unique_ptr<ErrnoException> inner(new ErrnoException({}, error)); throw SubProcessFailedToStartException(std::move(message), @@ -80,54 +62,45 @@ void PosixSpawnSubProcessImpl::PlatformCreateProcess( }; posix_spawn_file_actions_t file_actions; - error = posix_spawn_file_actions_init(&file_actions); - check_error(u"Failed to call posix_spawn_file_actions_init."); + check_error(posix_spawn_file_actions_init(&file_actions), + u"Failed to call posix_spawn_file_actions_init."); Guard file_actions_guard( [&file_actions] { posix_spawn_file_actions_destroy(&file_actions); }); - // dup2 stdin/stdout/stderr - error = posix_spawn_file_actions_adddup2( - &file_actions, stdin_pipe_.GetOtherFileDescriptor(), STDIN_FILENO); - check_error(u"Failed to call posix_spawn_file_actions_adddup2 on stdin."); - error = posix_spawn_file_actions_adddup2( - &file_actions, stdout_pipe_.GetOtherFileDescriptor(), STDOUT_FILENO); - check_error(u"Failed to call posix_spawn_file_actions_adddup2 on stdout."); - error = posix_spawn_file_actions_adddup2( - &file_actions, stderr_pipe_.GetOtherFileDescriptor(), STDERR_FILENO); - check_error(u"Failed to call posix_spawn_file_actions_adddup2 on stderr."); - - error = posix_spawn_file_actions_addclose( - &file_actions, stdin_pipe_.GetOtherFileDescriptor()); - check_error( - u"Failed to call posix_spawn_file_actions_addclose on self fd of stdin."); - error = posix_spawn_file_actions_addclose( - &file_actions, stdout_pipe_.GetOtherFileDescriptor()); - check_error( - u"Failed to call posix_spawn_file_actions_addclose on self fd stdout."); - error = posix_spawn_file_actions_addclose( - &file_actions, stderr_pipe_.GetOtherFileDescriptor()); - check_error( - u"Failed to call posix_spawn_file_actions_addclose on self fd stderr."); + auto add_dup2 = [&check_error, &file_actions](int from, int to, + StringView name) { + check_error( + posix_spawn_file_actions_adddup2(&file_actions, from, to), + Format(u"Failed to call posix_spawn_file_actions_adddup2 on {}.", + name)); + }; - error = posix_spawn_file_actions_addclose( - &file_actions, stdin_pipe_.GetSelfFileDescriptor()); - check_error( - u"Failed to call posix_spawn_file_actions_addclose on parent fd of " - u"stdin."); - error = posix_spawn_file_actions_addclose( - &file_actions, stdout_pipe_.GetSelfFileDescriptor()); - check_error( - u"Failed to call posix_spawn_file_actions_addclose on parent fd of " - u"stdout."); - error = posix_spawn_file_actions_addclose( - &file_actions, stderr_pipe_.GetSelfFileDescriptor()); - check_error( - u"Failed to call posix_spawn_file_actions_addclose on parent fd of " - u"stderr."); + auto add_close = [&check_error, &file_actions]( + UniDirectionalUnixPipeResult& pipe, StringView name) { + check_error(posix_spawn_file_actions_addclose(&file_actions, pipe.read), + Format(u"Failed to call posix_spawn_file_actions_addclose on " + u"read end of pipe {}.", + name)); + check_error(posix_spawn_file_actions_addclose(&file_actions, pipe.write), + Format(u"Failed to call posix_spawn_file_actions_addclose on " + u"write end of pipe {}.", + name)); + }; + + auto my_stdin = OpenUniDirectionalPipe(); + auto my_stdout = OpenUniDirectionalPipe(); + auto my_stderr = OpenUniDirectionalPipe(); + + add_dup2(my_stdin.read, STDIN_FILENO, u"stdin"); + add_dup2(my_stdout.write, STDOUT_FILENO, u"stdout"); + add_dup2(my_stderr.write, STDERR_FILENO, u"stderr"); + add_close(my_stdin, u"stdin"); + add_close(my_stdout, u"stdout"); + add_close(my_stderr, u"stderr"); posix_spawnattr_t attr; - error = posix_spawnattr_init(&attr); - check_error(u"Failed to call posix_spawnattr_init."); + check_error(posix_spawnattr_init(&attr), + u"Failed to call posix_spawnattr_init."); Guard attr_guard([&attr] { posix_spawnattr_destroy(&attr); }); #ifdef CRU_PLATFORM_OSX @@ -146,15 +119,21 @@ void PosixSpawnSubProcessImpl::PlatformCreateProcess( auto envp = CreateCstrArray(start_info.environments); Guard envp_guard([envp] { DestroyCstrArray(envp); }); - error = posix_spawnp(&pid_, exe.c_str(), &file_actions, &attr, argv, envp); - check_error(u"Failed to call posix_spawnp."); + check_error( + posix_spawnp(&pid_, exe.c_str(), &file_actions, &attr, argv, envp), + u"Failed to call posix_spawnp."); - error = ::close(stdin_pipe_.GetOtherFileDescriptor()); - check_error(u"Failed to close child stdin."); - error = ::close(stdout_pipe_.GetOtherFileDescriptor()); - check_error(u"Failed to close child stdout."); - error = ::close(stderr_pipe_.GetOtherFileDescriptor()); - check_error(u"Failed to close child stderr."); + stdin_stream_ = std::make_unique<UnixFileStream>(std::move(my_stdin.write), + false, false, true); + stdout_stream_ = std::make_unique<UnixFileStream>(std::move(my_stdout.read), + false, true, false); + stderr_stream_ = std::make_unique<UnixFileStream>(std::move(my_stderr.read), + false, true, false); + + stdout_buffer_stream_ = + std::make_unique<io::AutoReadStream>(stdout_stream_.get(), true, false); + stderr_buffer_stream_ = + std::make_unique<io::AutoReadStream>(stderr_stream_.get(), true, false); } SubProcessExitResult PosixSpawnSubProcessImpl::PlatformWaitForProcess() { diff --git a/src/base/platform/unix/UnixFile.cpp b/src/base/platform/unix/UnixFile.cpp new file mode 100644 index 00000000..b9af109a --- /dev/null +++ b/src/base/platform/unix/UnixFile.cpp @@ -0,0 +1,96 @@ +#include "cru/base/platform/unix/UnixFile.h" +#include "cru/base/Exception.h" +#include "cru/base/log/Logger.h" + +#include <fcntl.h> +#include <sys/fcntl.h> +#include <unistd.h> + +namespace cru::platform::unix { + +UnixFileDescriptor::UnixFileDescriptor() + : descriptor_(-1), auto_close_(false) {} + +UnixFileDescriptor::UnixFileDescriptor(int descriptor, bool auto_close, + std::function<int(int)> close) + : descriptor_(descriptor), + auto_close_(auto_close), + close_(std::move(close)) {} + +UnixFileDescriptor::~UnixFileDescriptor() { + constexpr auto kLogTag = u"cru::platform::unix::UnixFileDescriptor"; + if (this->IsValid() && this->IsAutoClose()) { + if (!this->DoClose()) { + CRU_LOG_TAG_ERROR(u"Failed to close file descriptor {}, errno {}.", + descriptor_, errno); + } + } +} + +UnixFileDescriptor::UnixFileDescriptor(UnixFileDescriptor&& other) noexcept + : descriptor_(other.descriptor_), + auto_close_(other.auto_close_), + close_(std::move(other.close_)) { + other.descriptor_ = -1; + other.auto_close_ = false; + other.close_ = nullptr; +} + +UnixFileDescriptor& UnixFileDescriptor::operator=( + UnixFileDescriptor&& other) noexcept { + if (this != &other) { + if (this->IsValid()) { + this->Close(); + } + this->descriptor_ = other.descriptor_; + this->auto_close_ = other.auto_close_; + this->close_ = other.close_; + other.descriptor_ = -1; + other.auto_close_ = false; + other.close_ = nullptr; + } + return *this; +} + +bool UnixFileDescriptor::IsValid() const { return this->descriptor_ >= 0; } + +int UnixFileDescriptor::GetValue() const { + if (!this->IsValid()) { + throw Exception("Can't get value of an invalid unix file descriptor."); + } + return this->descriptor_; +} + +void UnixFileDescriptor::Close() { + if (!this->IsValid()) { + throw Exception("Can't close an invalid unix file descriptor."); + } + if (!this->DoClose()) { + throw ErrnoException(u"Failed to call close on file descriptor."); + } + descriptor_ = -1; + auto_close_ = false; +} + +bool UnixFileDescriptor::DoClose() { + if (this->close_) { + return this->close_(this->descriptor_) == 0; + } else { + return ::close(this->descriptor_) == 0; + } +} + +UniDirectionalUnixPipeResult OpenUniDirectionalPipe(UnixPipeFlag flags) { + int fds[2]; + if (::pipe(fds) != 0) { + throw ErrnoException(u"Failed to create unix pipe."); + } + + if (flags & UnixPipeFlags::NonBlock) { + ::fcntl(fds[0], F_SETFL, O_NONBLOCK); + ::fcntl(fds[1], F_SETFL, O_NONBLOCK); + } + + return {UnixFileDescriptor(fds[0]), UnixFileDescriptor(fds[1])}; +} +} // namespace cru::platform::unix diff --git a/src/base/platform/unix/UnixFileStream.cpp b/src/base/platform/unix/UnixFileStream.cpp index 6d8bab25..e1a6080b 100644 --- a/src/base/platform/unix/UnixFileStream.cpp +++ b/src/base/platform/unix/UnixFileStream.cpp @@ -2,12 +2,10 @@ #include "cru/base/Exception.h" #include "cru/base/Format.h" #include "cru/base/io/Stream.h" -#include "cru/base/log/Logger.h" #include <fcntl.h> #include <sys/fcntl.h> #include <unistd.h> -#include <cerrno> namespace cru::platform::unix { using namespace cru::io; @@ -41,7 +39,7 @@ int MapSeekOrigin(Stream::SeekOrigin origin) { } // namespace UnixFileStream::UnixFileStream(const char *path, int oflag, mode_t mode) { - file_descriptor_ = ::open(path, oflag, mode); + file_descriptor_ = UnixFileDescriptor(::open(path, oflag, mode)); if (file_descriptor_ == -1) { throw ErrnoException( Format(u"Failed to open file {} with oflag {}, mode {}.", @@ -50,16 +48,11 @@ UnixFileStream::UnixFileStream(const char *path, int oflag, mode_t mode) { SetSupportedOperations( {OflagCanSeek(oflag), OflagCanRead(oflag), OflagCanWrite(oflag)}); - - auto_close_ = true; } -UnixFileStream::UnixFileStream(int fd, bool can_seek, bool can_read, - bool can_write, bool auto_close) - : Stream(can_seek, can_read, can_write) { - file_descriptor_ = fd; - auto_close_ = auto_close; -} +UnixFileStream::UnixFileStream(UnixFileDescriptor fd, bool can_seek, + bool can_read, bool can_write) + : Stream(can_seek, can_read, can_write), file_descriptor_(std::move(fd)) {} UnixFileStream::~UnixFileStream() { DoClose(); } @@ -90,9 +83,8 @@ Index UnixFileStream::DoWrite(const std::byte *buffer, Index offset, void UnixFileStream::DoClose() { CRU_STREAM_BEGIN_CLOSE - if (auto_close_ && ::close(file_descriptor_) == -1) { - throw ErrnoException(u"Failed to close file."); + if (file_descriptor_) { + file_descriptor_ = {}; } - file_descriptor_ = -1; } } // namespace cru::platform::unix diff --git a/src/base/platform/unix/UnixPipe.cpp b/src/base/platform/unix/UnixPipe.cpp deleted file mode 100644 index f512c2f4..00000000 --- a/src/base/platform/unix/UnixPipe.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "cru/base/platform/unix/UnixPipe.h" -#include "cru/base/Exception.h" -#include "cru/base/log/Logger.h" - -#include <fcntl.h> -#include <sys/fcntl.h> -#include <unistd.h> - -namespace cru::platform::unix { -UnixPipe::UnixPipe(Usage usage, bool auto_close, UnixPipeFlag flags) - : usage_(usage), auto_close_(auto_close), flags_(flags) { - int fds[2]; - if (pipe(fds) != 0) { - throw ErrnoException(u"Failed to create unix pipe."); - } - - if (flags & UnixPipeFlags::NonBlock) { - fcntl(fds[0], F_SETFL, O_NONBLOCK); - fcntl(fds[1], F_SETFL, O_NONBLOCK); - } - - read_fd_ = fds[0]; - write_fd_ = fds[1]; -} - -int UnixPipe::GetSelfFileDescriptor() { - if (usage_ == Usage::Send) { - return write_fd_; - } else { - return read_fd_; - } -} - -int UnixPipe::GetOtherFileDescriptor() { - if (usage_ == Usage::Send) { - return read_fd_; - } else { - return write_fd_; - } -} - -UnixPipe::~UnixPipe() { - if (auto_close_) { - auto self_fd = GetSelfFileDescriptor(); - if (::close(self_fd) != 0) { - CRU_LOG_TAG_ERROR(u"Failed to close unix pipe file descriptor {}, errno {}.", - self_fd, errno); - } - } -} -} // namespace cru::platform::unix diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index 61222a68..683c8295 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -25,6 +25,7 @@ target_compile_definitions(CruBaseTest PRIVATE if (UNIX AND NOT EMSCRIPTEN) target_sources(CruBaseTest PRIVATE + platform/unix/UnixFileTest.cpp platform/unix/UnixFileStreamTest.cpp ) endif() diff --git a/test/common/platform/unix/UnixFileTest.cpp b/test/common/platform/unix/UnixFileTest.cpp new file mode 100644 index 00000000..d5bba0db --- /dev/null +++ b/test/common/platform/unix/UnixFileTest.cpp @@ -0,0 +1,38 @@ + +#include "cru/base/platform/unix/UnixFileStream.h" + +#include <catch2/catch_test_macros.hpp> + +#include <fcntl.h> +#include <filesystem> + +TEST_CASE("UnixFile Work", "[unix]") { + using namespace cru; + using namespace cru::platform::unix; + + auto calledTimes = 0; + auto mockClose = [&calledTimes](int _) { + calledTimes += 1; + return 0; + }; + + auto temp_file_path = + (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") + .generic_string(); + mkstemp(temp_file_path.data()); + + auto fdNumber = ::open(temp_file_path.c_str(), O_WRONLY | O_CREAT); + + { + UnixFileDescriptor fd(fdNumber, true, std::move(mockClose)); + REQUIRE(calledTimes == 0); + UnixFileDescriptor fd2(std::move(fd)); + REQUIRE(calledTimes == 0); + UnixFileDescriptor fd3; + fd3 = std::move(fd2); + REQUIRE(calledTimes == 0); + } + REQUIRE(calledTimes == 1); + + std::filesystem::remove(temp_file_path); +} |