aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver-test/process-tests.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-04-23 18:16:57 +0200
committerStefan Boberg <[email protected]>2026-04-23 18:16:57 +0200
commit0232b991cd7d8e3a2114ea30e4591dd3e7b65c36 (patch)
tree94730e7594fd09ae1fa820391ce311f6daf13905 /src/zenserver-test/process-tests.cpp
parentFix forward declaration order for s_GotSigWinch and SigWinchHandler (diff)
parenttrace: declare Region event name fields as AnsiString (#1012) (diff)
downloadarchived-zen-sb/zen-help.tar.xz
archived-zen-sb/zen-help.zip
Merge branch 'main' into sb/zen-helpsb/zen-help
- Combine HelpCommand (this branch) with HistoryCommand (main) in zen CLI dispatcher - Keep filter-aware TuiPickOne rewrite; adopt main's ASCII arrow glyphs in doc comment
Diffstat (limited to 'src/zenserver-test/process-tests.cpp')
-rw-r--r--src/zenserver-test/process-tests.cpp140
1 files changed, 138 insertions, 2 deletions
diff --git a/src/zenserver-test/process-tests.cpp b/src/zenserver-test/process-tests.cpp
index ae11bb294..16af1879a 100644
--- a/src/zenserver-test/process-tests.cpp
+++ b/src/zenserver-test/process-tests.cpp
@@ -13,6 +13,7 @@
# if ZEN_PLATFORM_WINDOWS
# include <zencore/windows.h>
# else
+# include <cerrno>
# include <unistd.h>
# endif
@@ -51,6 +52,45 @@ ReadAllFromPipe(StdoutPipeHandles& Pipe)
return Result;
}
+// Write all of @p Data to the write end of a StdinPipeHandles. Returns true on success.
+static bool
+WriteAllToPipe(StdinPipeHandles& Pipe, std::string_view Data)
+{
+# if ZEN_PLATFORM_WINDOWS
+ const char* Ptr = Data.data();
+ size_t Remaining = Data.size();
+ while (Remaining > 0)
+ {
+ DWORD BytesWritten = 0;
+ if (!::WriteFile(Pipe.WriteHandle, Ptr, static_cast<DWORD>(Remaining), &BytesWritten, nullptr) || BytesWritten == 0)
+ {
+ return false;
+ }
+ Ptr += BytesWritten;
+ Remaining -= BytesWritten;
+ }
+ return true;
+# else
+ const char* Ptr = Data.data();
+ size_t Remaining = Data.size();
+ while (Remaining > 0)
+ {
+ ssize_t BytesWritten = write(Pipe.WriteFd, Ptr, Remaining);
+ if (BytesWritten <= 0)
+ {
+ if (BytesWritten < 0 && errno == EINTR)
+ {
+ continue;
+ }
+ return false;
+ }
+ Ptr += BytesWritten;
+ Remaining -= static_cast<size_t>(BytesWritten);
+ }
+ return true;
+# endif
+}
+
TEST_SUITE_BEGIN("server.process");
//////////////////////////////////////////////////////////////////////////
@@ -115,7 +155,7 @@ TEST_CASE("pipe.raii_cleanup")
{
StdoutPipeHandles Pipe;
REQUIRE(CreateStdoutPipe(Pipe));
- // Pipe goes out of scope here — destructor should close both ends
+ // Pipe goes out of scope here - destructor should close both ends
}
}
@@ -155,7 +195,7 @@ TEST_CASE("pipe.move_semantics")
CHECK(Moved.WriteFd == -1);
# endif
- // Assigned goes out of scope — destructor closes handles
+ // Assigned goes out of scope - destructor closes handles
}
TEST_CASE("pipe.close_is_idempotent")
@@ -276,6 +316,102 @@ TEST_CASE("pipe.separate_stderr")
//////////////////////////////////////////////////////////////////////////
+TEST_CASE("stdin_pipe.echo_round_trip")
+{
+ StdinPipeHandles StdinPipe;
+ StdoutPipeHandles StdoutPipe;
+ REQUIRE(CreateStdinPipe(StdinPipe));
+ REQUIRE(CreateStdoutPipe(StdoutPipe));
+
+ const std::string Input = "hello-from-stdin\nsecond-line\n";
+ std::filesystem::path AppStub = GetAppStubPath();
+ std::string CommandLine = "zentest-appstub -stdin_echo";
+
+ CreateProcOptions Options;
+ Options.StdinPipe = &StdinPipe;
+ Options.StdoutPipe = &StdoutPipe;
+
+ ProcessHandle Process(CreateProc(AppStub, CommandLine, Options));
+
+ // Close the read end (child side) so that when we finish writing and close the write end,
+ // the child sees EOF. Also close the stdout write end so our reads see EOF after the child exits.
+ StdinPipe.CloseReadEnd();
+ StdoutPipe.CloseWriteEnd();
+
+ REQUIRE(WriteAllToPipe(StdinPipe, Input));
+ StdinPipe.CloseWriteEnd(); // signals EOF to the child
+
+ std::string Output = ReadAllFromPipe(StdoutPipe);
+
+ Process.Wait();
+
+ CHECK(Output.find(Input) != std::string::npos);
+ CHECK_EQ(Process.GetExitCode(), 0);
+}
+
+TEST_CASE("stdin_pipe.raii_cleanup")
+{
+ for (int i = 0; i < 100; ++i)
+ {
+ StdinPipeHandles Pipe;
+ REQUIRE(CreateStdinPipe(Pipe));
+ }
+}
+
+TEST_CASE("stdin_pipe.move_semantics")
+{
+ StdinPipeHandles Original;
+ REQUIRE(CreateStdinPipe(Original));
+
+ StdinPipeHandles Moved(std::move(Original));
+
+# if ZEN_PLATFORM_WINDOWS
+ CHECK(Moved.ReadHandle != nullptr);
+ CHECK(Moved.WriteHandle != nullptr);
+ CHECK(Original.ReadHandle == nullptr);
+ CHECK(Original.WriteHandle == nullptr);
+# else
+ CHECK(Moved.ReadFd >= 0);
+ CHECK(Moved.WriteFd >= 0);
+ CHECK(Original.ReadFd == -1);
+ CHECK(Original.WriteFd == -1);
+# endif
+
+ StdinPipeHandles Assigned;
+ Assigned = std::move(Moved);
+
+# if ZEN_PLATFORM_WINDOWS
+ CHECK(Assigned.ReadHandle != nullptr);
+ CHECK(Assigned.WriteHandle != nullptr);
+ CHECK(Moved.ReadHandle == nullptr);
+ CHECK(Moved.WriteHandle == nullptr);
+# else
+ CHECK(Assigned.ReadFd >= 0);
+ CHECK(Assigned.WriteFd >= 0);
+ CHECK(Moved.ReadFd == -1);
+ CHECK(Moved.WriteFd == -1);
+# endif
+}
+
+TEST_CASE("stdin_pipe.close_is_idempotent")
+{
+ StdinPipeHandles Pipe;
+ REQUIRE(CreateStdinPipe(Pipe));
+
+ Pipe.Close();
+ Pipe.Close();
+
+# if ZEN_PLATFORM_WINDOWS
+ CHECK(Pipe.ReadHandle == nullptr);
+ CHECK(Pipe.WriteHandle == nullptr);
+# else
+ CHECK(Pipe.ReadFd == -1);
+ CHECK(Pipe.WriteFd == -1);
+# endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+
TEST_SUITE_END();
} // namespace zen::tests