#include 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); }