diff --git a/lib/utils/mu-test-utils.cc b/lib/utils/mu-test-utils.cc index c6f728bc..eb9579e1 100644 --- a/lib/utils/mu-test-utils.cc +++ b/lib/utils/mu-test-utils.cc @@ -115,24 +115,6 @@ Mu::allow_warnings() {}); } - - -Result -Mu::run_mu_command(const std::string& args) -{ - auto cmdline{mu_format("/bin/sh -c '{} {}'", MU_PROGRAM, args)}; - if (g_test_verbose()) - mu_println("command-line: {}", cmdline); - - GError *err{}; - int wait_status{}; - if (!g_spawn_command_line_sync(cmdline.c_str(), {}, {}, &wait_status, &err)) - return Err(Error::Code::File, &err, "failed to execute command '{}", cmdline); - else - return Ok(WEXITSTATUS(wait_status)); -} - - Mu::TempDir::TempDir(bool autodelete): autodelete_{autodelete} { if (auto res{make_temp_dir()}; !res) diff --git a/lib/utils/mu-test-utils.hh b/lib/utils/mu-test-utils.hh index c1368da3..6537046f 100644 --- a/lib/utils/mu-test-utils.hh +++ b/lib/utils/mu-test-utils.hh @@ -20,6 +20,7 @@ #ifndef MU_TEST_UTILS_HH__ #define MU_TEST_UTILS_HH__ +#include #include #include #include @@ -91,6 +92,14 @@ bool set_en_us_utf8_locale(); } \ } while(0) +#define assert_valid_command(RCO) do { \ + assert_valid_result(RCO); \ + if ((RCO)->exit_code != 0 && !(RCO)->standard_err.empty()) \ + mu_printerrln("{}:{}: {}", \ + __FILE__, __LINE__, (RCO)->standard_err); \ + g_assert_cmpuint((RCO)->exit_code, ==, 0); \ +} while (0) + /** * For unit-tests, allow warnings in the current function. * @@ -98,18 +107,6 @@ bool set_en_us_utf8_locale(); void allow_warnings(); - -/** - * Execute the in-tree mu executable with the arguments - * Asserts if fails. - * - * @param args arguments;; - * - * @return either the exit code or an error. - */ -Result run_mu_command(const std::string& args); - - /** * For unit-tests, a RAII tempdir. * diff --git a/lib/utils/mu-utils-file.cc b/lib/utils/mu-utils-file.cc index 82399ebb..3f5039a4 100644 --- a/lib/utils/mu-utils-file.cc +++ b/lib/utils/mu-utils-file.cc @@ -277,6 +277,37 @@ Mu::read_from_stdin() g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(outmem))}); } +Result +Mu::run_command(std::initializer_list args) +{ + std::vector argvec{}; + for (auto&& arg: args) + argvec.push_back(g_strdup(arg.c_str())); + argvec.push_back({}); + + GError *err{}; + int wait_status{}; + gchar *std_out{}, *std_err{}; + auto res = g_spawn_sync({}, + static_cast(argvec.data()), + {}, + (GSpawnFlags)(G_SPAWN_SEARCH_PATH), + {}, {}, + &std_out, &std_err, + &wait_status, &err); + + for (auto& a: argvec) + g_free(a); + + if (!res) + return Err(Error::Code::File, &err, "failed to execute command"); + else + return Ok(Mu::CommandOutput{ + WEXITSTATUS(wait_status), + to_string_gchar(std::move(std_out/*consumed*/)), + to_string_gchar(std::move(std_err/*consumed*/))}); +} + #ifdef BUILD_TESTS diff --git a/lib/utils/mu-utils-file.hh b/lib/utils/mu-utils-file.hh index c922a68c..c9d89146 100644 --- a/lib/utils/mu-utils-file.hh +++ b/lib/utils/mu-utils-file.hh @@ -196,6 +196,22 @@ Result make_temp_dir(); Result remove_directory(const std::string& path); +/** + * Run some system command + * + * @param cmd the command + * + * @return Ok(exit code) or an error. Note that exit-code != 0 is _not_ + * considered an error from the perspective of this function. + */ +struct CommandOutput { + int exit_code; + std::string standard_out; + std::string standard_err; +}; +Result run_command(std::initializer_list args); + + } // namespace Mu #endif /* MU_UTILS_FILE_HH__ */