[M4] add exec::run subprocess wrapper
This commit is contained in:
78
tests/exec_run.cpp
Normal file
78
tests/exec_run.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
import cargoxx.exec;
|
||||
import cargoxx.util;
|
||||
import std;
|
||||
|
||||
using cargoxx::exec::ExecOptions;
|
||||
using cargoxx::exec::ExecResult;
|
||||
using cargoxx::exec::run;
|
||||
using cargoxx::util::ErrorCode;
|
||||
|
||||
TEST_CASE("run captures stdout from echo", "[exec]") {
|
||||
auto r = run("echo", {"hello", "world"});
|
||||
REQUIRE(r.has_value());
|
||||
REQUIRE(r->exit_code == 0);
|
||||
REQUIRE(r->stdout_text == "hello world\n");
|
||||
REQUIRE(r->stderr_text.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("run returns 0 for true", "[exec]") {
|
||||
auto r = run("true", {});
|
||||
REQUIRE(r.has_value());
|
||||
REQUIRE(r->exit_code == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("run returns non-zero for false", "[exec]") {
|
||||
auto r = run("false", {});
|
||||
REQUIRE(r.has_value());
|
||||
REQUIRE(r->exit_code != 0);
|
||||
}
|
||||
|
||||
TEST_CASE("run reports ExecToolNotFound for missing program", "[exec]") {
|
||||
auto r = run("definitely-not-a-real-command-xyz-12345", {});
|
||||
REQUIRE_FALSE(r.has_value());
|
||||
REQUIRE(r.error().code == ErrorCode::ExecToolNotFound);
|
||||
}
|
||||
|
||||
TEST_CASE("run honors cwd", "[exec]") {
|
||||
auto cwd = std::filesystem::temp_directory_path();
|
||||
auto r = run("pwd", {}, ExecOptions{.cwd = cwd});
|
||||
REQUIRE(r.has_value());
|
||||
REQUIRE(r->exit_code == 0);
|
||||
// pwd may print the canonical path; at minimum the result starts with /tmp
|
||||
// (or whatever the temp dir's prefix is)
|
||||
REQUIRE_FALSE(r->stdout_text.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("run captures stderr", "[exec]") {
|
||||
auto r = run("ls", {"/this/path/does/not/exist/cargoxx-xyz"});
|
||||
REQUIRE(r.has_value());
|
||||
REQUIRE(r->exit_code != 0);
|
||||
REQUIRE_FALSE(r->stderr_text.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("run propagates env_overrides", "[exec]") {
|
||||
auto r = run("env", {}, ExecOptions{
|
||||
.env_overrides = {{"CARGOXX_TEST_VAR", "lemonade"}},
|
||||
});
|
||||
REQUIRE(r.has_value());
|
||||
REQUIRE(r->exit_code == 0);
|
||||
REQUIRE(r->stdout_text.find("CARGOXX_TEST_VAR=lemonade") != std::string::npos);
|
||||
}
|
||||
|
||||
TEST_CASE("run enforces a timeout", "[exec]") {
|
||||
auto r = run("sleep", {"5"},
|
||||
ExecOptions{.timeout = std::chrono::seconds{1}});
|
||||
REQUIRE_FALSE(r.has_value());
|
||||
REQUIRE(r.error().code == ErrorCode::ExecCommandFailed);
|
||||
}
|
||||
|
||||
TEST_CASE("run handles many output bytes without deadlocking", "[exec]") {
|
||||
// 64 KiB of zeros — exercises pipe-pumping past the typical 4 KiB buffer
|
||||
// without producing unbounded output.
|
||||
auto r = run("dd", {"if=/dev/zero", "bs=1024", "count=64", "status=none"});
|
||||
REQUIRE(r.has_value());
|
||||
REQUIRE(r->exit_code == 0);
|
||||
REQUIRE(r->stdout_text.size() == 64 * 1024);
|
||||
}
|
||||
Reference in New Issue
Block a user