aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-09-02 22:29:11 +0800
committerYuqian Yang <crupest@crupest.life>2025-09-03 01:52:38 +0800
commit545a638929218a83d194402b3d52f5bffd87d9eb (patch)
treef659f671fec17f3ef7dced31f3a1f59673d18690
parent5035f18f44f675af2faa4019b6de14b3f3aab270 (diff)
downloadcru-545a638929218a83d194402b3d52f5bffd87d9eb.tar.gz
cru-545a638929218a83d194402b3d52f5bffd87d9eb.tar.bz2
cru-545a638929218a83d194402b3d52f5bffd87d9eb.zip
UnixFileDescriptor.
-rw-r--r--include/cru/base/platform/unix/PosixSpawnSubProcess.h6
-rw-r--r--include/cru/base/platform/unix/UnixFile.h61
-rw-r--r--include/cru/base/platform/unix/UnixFileStream.h8
-rw-r--r--include/cru/base/platform/unix/UnixPipe.h68
-rw-r--r--src/base/CMakeLists.txt2
-rw-r--r--src/base/platform/unix/PosixSpawnSubProcess.cpp119
-rw-r--r--src/base/platform/unix/UnixFile.cpp96
-rw-r--r--src/base/platform/unix/UnixFileStream.cpp20
-rw-r--r--src/base/platform/unix/UnixPipe.cpp51
-rw-r--r--test/common/CMakeLists.txt1
-rw-r--r--test/common/platform/unix/UnixFileTest.cpp38
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);
+}