/// Executes the cleanup of a test case. /// /// \param test_program Path to the test program to execute. /// \param test_case Name of the test case to run. /// \param user_variables Set of configuration variables to pass to the test. static void exec_cleanup(const char* test_program, const char* test_case, const char* const user_variables[]) { char* name; kyua_error_t error = kyua_text_printf(&name, "%s:cleanup", test_case); if (kyua_error_is_set(error)) kyua_error_err(EXIT_FAILURE, error, "Failed to construct argument list"); const size_t nargs = 1 /* test_program */ + + 2 * count_variables(user_variables) /* -v name=value */ + 1 /* test_case */ + 1 /* NULL */; const char** args = malloc(sizeof(const char*) * nargs); if (args == NULL) kyua_error_err(EXIT_FAILURE, kyua_oom_error_new(), "Failed to construct arguments list"); size_t i = 0; args[i++] = test_program; const char* const* iter; for (iter = user_variables; *iter != NULL; ++iter) { args[i++] = "-v"; args[i++] = *iter; } args[i++] = name; args[i++] = NULL; assert(i == nargs); kyua_run_exec(test_program, args); }
/// Body of a subprocess to execute GDB. /// /// This should be called from the child created by a kyua_run_fork() call, /// which means that we do not have to take care of isolating the process. /// /// \pre The caller must have flushed stdout before spawning this process, to /// prevent double-flushing and/or corruption of data. /// /// \param program Path to the program being debugged. Can be relative to /// the given work directory. /// \param core_name Path to the dumped core. Use find_core() to deduce /// a valid candidate. Can be relative to the given work directory. /// \param output Stream to which to send the output of GDB. static void run_gdb(const char* program, const char* core_name, FILE* output) { // TODO(jmmv): Should be done by kyua_run_fork(), but doing so would change // the semantics of the ATF interface. Need to evaluate this carefully. const kyua_error_t error = kyua_env_unset("TERM"); if (kyua_error_is_set(error)) { kyua_error_warn(error, "Failed to unset TERM; GDB may misbehave"); free(error); } (void)close(STDIN_FILENO); #if defined(__minix) && !defined(NDEBUG) const int input_fd = #endif /* defined(__minix) && !defined(NDEBUG) */ open("/dev/null", O_RDONLY); assert(input_fd == STDIN_FILENO); const int output_fd = fileno(output); assert(output_fd != -1); // We expect a file-backed stream. if (output_fd != STDOUT_FILENO) { fflush(stdout); (void)dup2(output_fd, STDOUT_FILENO); } if (output_fd != STDERR_FILENO) { fflush(stderr); (void)dup2(output_fd, STDERR_FILENO); } if (output_fd != STDOUT_FILENO && output_fd != STDERR_FILENO) fclose(output); const char* const gdb_args[] = { "gdb", "-batch", "-q", "-ex", "bt", program, core_name, NULL }; kyua_run_exec(kyua_stacktrace_gdb, gdb_args); }
/// Executes the body of a test case. /// /// \param test_program Path to the test program to execute. /// \param test_case Name of the test case to run. /// \param result_file Path to the ATF result file to be created. /// \param user_variables Set of configuration variables to pass to the test. static void exec_body(const char* test_program, const char* test_case, const char* result_file, const char* const user_variables[]) { const size_t nargs = 1 /* test_program */ + 2 /* -r result_file */ + 2 * count_variables(user_variables) /* -v name=value */ + 1 /* test_case */ + 1 /* NULL */; const char** args = malloc(sizeof(const char*) * nargs); if (args == NULL) kyua_error_err(EXIT_FAILURE, kyua_oom_error_new(), "Failed to construct arguments list"); size_t i = 0; args[i++] = test_program; args[i++] = "-r"; args[i++] = result_file; const char* const* iter; for (iter = user_variables; *iter != NULL; ++iter) { args[i++] = "-v"; args[i++] = *iter; } args[i++] = test_case; args[i++] = NULL; assert(i == nargs); kyua_run_exec(test_program, args); }
/// Executes the test program in list mode. /// /// \param test_program Path to the test program to execute; should be absolute. /// \param stdout_fds Pipe to write the output of the test program to. static void run_list(const char* test_program, const int stdout_fds[2]) { (void)close(stdout_fds[0]); if (stdout_fds[1] != STDOUT_FILENO) { if (dup2(stdout_fds[1], STDOUT_FILENO) == -1) err(EXIT_FAILURE, "dup2 failed"); (void)close(stdout_fds[1]); } if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) err(EXIT_FAILURE, "dup2 failed"); const char* const program_args[] = { test_program, "-l", NULL }; kyua_run_exec(test_program, program_args); }
/// Uses kyua_fork, kyua_exec and kyua_wait to execute a subprocess. /// /// \param program Path to the program to run. /// \param args Arguments to the program. /// \param [out] exitstatus The exit status of the subprocess, if it exits /// successfully without timing out nor receiving a signal. /// /// \return Returns the error code of kyua_run_wait (which should have the /// error representation of the exec call in the subprocess). static kyua_error_t exec_check(const char* program, const char* const* args, int* exitstatus) { kyua_run_params_t run_params; kyua_run_params_init(&run_params); pid_t pid; kyua_error_t error = kyua_run_fork(&run_params, &pid); if (!kyua_error_is_set(error) && pid == 0) kyua_run_exec(program, args); ATF_REQUIRE(!kyua_error_is_set(error)); int status; bool timed_out; error = kyua_run_wait(pid, &status, &timed_out); if (!kyua_error_is_set(error)) { ATF_REQUIRE(!timed_out); ATF_REQUIRE_MSG(WIFEXITED(status), "Subprocess expected to exit successfully"); *exitstatus = WEXITSTATUS(status); } return error; }