blob: 7bb6b8f56737e7f55d7a026d08eba1f7e4ecf8ea [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <runtests-utils/posix-run-test.h>
#include <errno.h>
#include <fcntl.h>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fbl/algorithm.h>
#include <fbl/auto_call.h>
#include <unittest/unittest.h>
namespace runtests {
namespace {
// A list of the names of environment variables names that we pass into the
// spawned test subprocess.
constexpr const char* const kAllowedEnvironmentVars[] = {
"TMPDIR",
"PATH",
// Paths to the symbolizer for various sanitizers.
"ASAN_SYMBOLIZER_PATH",
"LSAN_SYMBOLIZER_PATH",
"MSAN_SYMBOLIZER_PATH",
"UBSAN_SYMBOLIZER_PATH",
// From unittest.h. Set by RunAllTests().
TEST_ENV_NAME,
};
} // namespace
std::unique_ptr<Result> PosixRunTest(const char* argv[],
const char*, // output_dir
const char* output_filename) {
int status;
const char* path = argv[0];
FILE* output_file = nullptr;
// Initialize |file_actions|, which dictate what I/O will be performed in the
// launched process, and ensure its destruction on function exit.
posix_spawn_file_actions_t file_actions;
if ((status = posix_spawn_file_actions_init(&file_actions))) {
fprintf(stderr, "FAILURE: posix_spawn_file_actions_init failed: %s\n",
strerror(status));
return std::make_unique<Result>(path, FAILED_TO_LAUNCH, 0);
}
auto auto_tidy = fbl::MakeAutoCall([&] {
posix_spawn_file_actions_destroy(&file_actions);
if (output_file != nullptr) {
fclose(output_file);
}
});
// Construct the array of allowed environment variable strings of the
// form "<name>=<value>". The env_strings array just keeps the underlying
// std::string objects alive so the envp pointers remain valid.
std::string env_strings[fbl::count_of(kAllowedEnvironmentVars)];
const char* envp[fbl::count_of(env_strings) + 1]; // +1 for null terminator.
size_t i = 0;
for (const char* var : kAllowedEnvironmentVars) {
const char* val = getenv(var);
if (val) {
env_strings[i] = std::string(var) + "=" + val;
envp[i] = env_strings[i].c_str();
++i;
}
}
envp[i] = nullptr;
// Tee output.
if (output_filename != nullptr) {
output_file = fopen(output_filename, "w");
if (output_file == nullptr) {
return std::make_unique<Result>(path, FAILED_DURING_IO, 0);
}
if ((status = posix_spawn_file_actions_addopen(
&file_actions, STDOUT_FILENO, output_filename,
O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
fprintf(stderr, "FAILURE: posix_spawn_file_actions_addopen failed: %s\n",
strerror(status));
return std::make_unique<Result>(path, FAILED_TO_LAUNCH, 0);
}
if ((status = posix_spawn_file_actions_adddup2(&file_actions, STDOUT_FILENO,
STDERR_FILENO))) {
fprintf(stderr, "FAILURE: posix_spawn_file_actions_adddup2 failed: %s\n",
strerror(status));
return std::make_unique<Result>(path, FAILED_TO_LAUNCH, 0);
}
}
// Launch the test subprocess.
pid_t test_pid;
if ((status = posix_spawn(&test_pid, path, &file_actions, nullptr,
const_cast<char**>(argv),
const_cast<char**>(envp)))) {
fprintf(stderr, "FAILURE: posix_spawn failed: %s\n", strerror(status));
return std::make_unique<Result>(path, FAILED_TO_LAUNCH, 0);
}
if (waitpid(test_pid, &status, WUNTRACED | WCONTINUED) == -1) {
fprintf(stderr, "FAILURE: waitpid failed: %s\n", strerror(errno));
return std::make_unique<Result>(path, FAILED_TO_WAIT, 0);
}
if (WIFEXITED(status)) {
int return_code = WEXITSTATUS(status);
LaunchStatus launch_status =
return_code ? FAILED_NONZERO_RETURN_CODE : SUCCESS;
return std::make_unique<Result>(path, launch_status, return_code);
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "FAILURE: test process killed by signal %d\n", WTERMSIG(status));
return std::make_unique<Result>(path, FAILED_NONZERO_RETURN_CODE, 1);
}
if (WIFSTOPPED(status)) {
fprintf(stderr, "FAILURE: test process stopped by signal %d\n", WSTOPSIG(status));
return std::make_unique<Result>(path, FAILED_NONZERO_RETURN_CODE, 1);
}
fprintf(stderr, "FAILURE: test process with unexpected status: %#x", status);
return std::make_unique<Result>(path, FAILED_UNKNOWN, 0);
}
} // namespace runtests