|
|
|
@ -64,6 +64,10 @@ |
|
|
|
|
# if GTEST_OS_FUCHSIA |
|
|
|
|
# include <lib/fdio/io.h> |
|
|
|
|
# include <lib/fdio/spawn.h> |
|
|
|
|
# include <lib/fdio/util.h> |
|
|
|
|
# include <lib/zx/socket.h> |
|
|
|
|
# include <lib/zx/port.h> |
|
|
|
|
# include <lib/zx/process.h> |
|
|
|
|
# include <zircon/processargs.h> |
|
|
|
|
# include <zircon/syscalls.h> |
|
|
|
|
# include <zircon/syscalls/port.h> |
|
|
|
@ -422,6 +426,9 @@ class DeathTestImpl : public DeathTest { |
|
|
|
|
// case of unexpected codes.
|
|
|
|
|
void ReadAndInterpretStatusByte(); |
|
|
|
|
|
|
|
|
|
// Returns stderr output from the child process.
|
|
|
|
|
virtual std::string GetErrorLogs(); |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
// The textual content of the code this object is testing. This class
|
|
|
|
|
// doesn't own this string and should not attempt to delete it.
|
|
|
|
@ -490,6 +497,10 @@ void DeathTestImpl::ReadAndInterpretStatusByte() { |
|
|
|
|
set_read_fd(-1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string DeathTestImpl::GetErrorLogs() { |
|
|
|
|
return GetCapturedStderr(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Signals that the death test code which should have exited, didn't.
|
|
|
|
|
// Should be called only in a death test child process.
|
|
|
|
|
// Writes a status byte to the child's status file descriptor, then
|
|
|
|
@ -558,7 +569,7 @@ bool DeathTestImpl::Passed(bool status_ok) { |
|
|
|
|
if (!spawned()) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
const std::string error_message = GetCapturedStderr(); |
|
|
|
|
const std::string error_message = GetErrorLogs(); |
|
|
|
|
|
|
|
|
|
bool success = false; |
|
|
|
|
Message buffer; |
|
|
|
@ -810,12 +821,6 @@ class FuchsiaDeathTest : public DeathTestImpl { |
|
|
|
|
const char* file, |
|
|
|
|
int line) |
|
|
|
|
: DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} |
|
|
|
|
virtual ~FuchsiaDeathTest() { |
|
|
|
|
zx_status_t status = zx_handle_close(child_process_); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status == ZX_OK); |
|
|
|
|
status = zx_handle_close(port_); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status == ZX_OK); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// All of these virtual functions are inherited from DeathTest.
|
|
|
|
|
virtual int Wait(); |
|
|
|
@ -826,9 +831,12 @@ class FuchsiaDeathTest : public DeathTestImpl { |
|
|
|
|
const char* const file_; |
|
|
|
|
// The line number on which the death test is located.
|
|
|
|
|
const int line_; |
|
|
|
|
// The stderr data captured by the child process.
|
|
|
|
|
std::string captured_stderr_; |
|
|
|
|
|
|
|
|
|
zx_handle_t child_process_ = ZX_HANDLE_INVALID; |
|
|
|
|
zx_handle_t port_ = ZX_HANDLE_INVALID; |
|
|
|
|
zx::process child_process_; |
|
|
|
|
zx::port port_; |
|
|
|
|
zx::socket stderr_socket_; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Utility class for accumulating command-line arguments.
|
|
|
|
@ -872,51 +880,74 @@ class Arguments { |
|
|
|
|
// status, or 0 if no child process exists. As a side effect, sets the
|
|
|
|
|
// outcome data member.
|
|
|
|
|
int FuchsiaDeathTest::Wait() { |
|
|
|
|
const int kProcessKey = 0; |
|
|
|
|
const int kSocketKey = 1; |
|
|
|
|
|
|
|
|
|
if (!spawned()) |
|
|
|
|
return 0; |
|
|
|
|
|
|
|
|
|
// Register to wait for the child process to terminate.
|
|
|
|
|
zx_status_t status_zx; |
|
|
|
|
status_zx = zx_object_wait_async(child_process_, |
|
|
|
|
port_, |
|
|
|
|
0 /* key */, |
|
|
|
|
ZX_PROCESS_TERMINATED, |
|
|
|
|
ZX_WAIT_ASYNC_ONCE); |
|
|
|
|
status_zx = child_process_.wait_async( |
|
|
|
|
port_, kProcessKey, ZX_PROCESS_TERMINATED, ZX_WAIT_ASYNC_ONCE); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); |
|
|
|
|
|
|
|
|
|
// Wait for it to terminate, or an exception to be received.
|
|
|
|
|
zx_port_packet_t packet; |
|
|
|
|
status_zx = zx_port_wait(port_, ZX_TIME_INFINITE, &packet); |
|
|
|
|
// Register to wait for the socket to be readable or closed.
|
|
|
|
|
status_zx = stderr_socket_.wait_async( |
|
|
|
|
port_, kSocketKey, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, |
|
|
|
|
ZX_WAIT_ASYNC_REPEATING); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); |
|
|
|
|
|
|
|
|
|
if (ZX_PKT_IS_EXCEPTION(packet.type)) { |
|
|
|
|
// Process encountered an exception. Kill it directly rather than letting
|
|
|
|
|
// other handlers process the event.
|
|
|
|
|
status_zx = zx_task_kill(child_process_); |
|
|
|
|
bool process_terminated = false; |
|
|
|
|
bool socket_closed = false; |
|
|
|
|
do { |
|
|
|
|
zx_port_packet_t packet = {}; |
|
|
|
|
status_zx = port_.wait(zx::time::infinite(), &packet); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); |
|
|
|
|
|
|
|
|
|
// Now wait for |child_process_| to terminate.
|
|
|
|
|
zx_signals_t signals = 0; |
|
|
|
|
status_zx = zx_object_wait_one( |
|
|
|
|
child_process_, ZX_PROCESS_TERMINATED, ZX_TIME_INFINITE, &signals); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(signals & ZX_PROCESS_TERMINATED); |
|
|
|
|
} else { |
|
|
|
|
// Process terminated.
|
|
|
|
|
GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type)); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_PROCESS_TERMINATED); |
|
|
|
|
} |
|
|
|
|
if (packet.key == kProcessKey) { |
|
|
|
|
if (ZX_PKT_IS_EXCEPTION(packet.type)) { |
|
|
|
|
// Process encountered an exception. Kill it directly rather than
|
|
|
|
|
// letting other handlers process the event. We will get a second
|
|
|
|
|
// kProcessKey event when the process actually terminates.
|
|
|
|
|
status_zx = child_process_.kill(); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); |
|
|
|
|
} else { |
|
|
|
|
// Process terminated.
|
|
|
|
|
GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_ONE(packet.type)); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_PROCESS_TERMINATED); |
|
|
|
|
process_terminated = true; |
|
|
|
|
} |
|
|
|
|
} else if (packet.key == kSocketKey) { |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(ZX_PKT_IS_SIGNAL_REP(packet.type)); |
|
|
|
|
if (packet.signal.observed & ZX_SOCKET_READABLE) { |
|
|
|
|
// Read data from the socket.
|
|
|
|
|
constexpr size_t kBufferSize = 1024; |
|
|
|
|
do { |
|
|
|
|
size_t old_length = captured_stderr_.length(); |
|
|
|
|
size_t bytes_read = 0; |
|
|
|
|
captured_stderr_.resize(old_length + kBufferSize); |
|
|
|
|
status_zx = stderr_socket_.read( |
|
|
|
|
0, &captured_stderr_.front() + old_length, kBufferSize, |
|
|
|
|
&bytes_read); |
|
|
|
|
captured_stderr_.resize(old_length + bytes_read); |
|
|
|
|
} while (status_zx == ZX_OK); |
|
|
|
|
if (status_zx == ZX_ERR_PEER_CLOSED) { |
|
|
|
|
socket_closed = true; |
|
|
|
|
} else { |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status_zx == ZX_ERR_SHOULD_WAIT); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(packet.signal.observed & ZX_SOCKET_PEER_CLOSED); |
|
|
|
|
socket_closed = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} while (!process_terminated && !socket_closed); |
|
|
|
|
|
|
|
|
|
ReadAndInterpretStatusByte(); |
|
|
|
|
|
|
|
|
|
zx_info_process_t buffer; |
|
|
|
|
status_zx = zx_object_get_info( |
|
|
|
|
child_process_, |
|
|
|
|
ZX_INFO_PROCESS, |
|
|
|
|
&buffer, |
|
|
|
|
sizeof(buffer), |
|
|
|
|
nullptr, |
|
|
|
|
nullptr); |
|
|
|
|
status_zx = child_process_.get_info( |
|
|
|
|
ZX_INFO_PROCESS, &buffer, sizeof(buffer), nullptr, nullptr); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status_zx == ZX_OK); |
|
|
|
|
|
|
|
|
|
GTEST_DEATH_TEST_CHECK_(buffer.exited); |
|
|
|
@ -943,7 +974,6 @@ DeathTest::TestRole FuchsiaDeathTest::AssumeRole() { |
|
|
|
|
return EXECUTE_TEST; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
CaptureStderr(); |
|
|
|
|
// Flush the log buffers since the log streams are shared with the child.
|
|
|
|
|
FlushInfoLog(); |
|
|
|
|
|
|
|
|
@ -970,29 +1000,55 @@ DeathTest::TestRole FuchsiaDeathTest::AssumeRole() { |
|
|
|
|
set_read_fd(status); |
|
|
|
|
|
|
|
|
|
// Set the pipe handle for the child.
|
|
|
|
|
fdio_spawn_action_t add_handle_action = {}; |
|
|
|
|
add_handle_action.action = FDIO_SPAWN_ACTION_ADD_HANDLE; |
|
|
|
|
add_handle_action.h.id = PA_HND(type, kFuchsiaReadPipeFd); |
|
|
|
|
add_handle_action.h.handle = child_pipe_handle; |
|
|
|
|
fdio_spawn_action_t spawn_actions[2] = {}; |
|
|
|
|
fdio_spawn_action_t* add_handle_action = &spawn_actions[0]; |
|
|
|
|
add_handle_action->action = FDIO_SPAWN_ACTION_ADD_HANDLE; |
|
|
|
|
add_handle_action->h.id = PA_HND(type, kFuchsiaReadPipeFd); |
|
|
|
|
add_handle_action->h.handle = child_pipe_handle; |
|
|
|
|
|
|
|
|
|
// Create a socket pair will be used to receive the child process' stderr.
|
|
|
|
|
zx::socket stderr_producer_socket; |
|
|
|
|
status = |
|
|
|
|
zx::socket::create(0, &stderr_producer_socket, &stderr_socket_); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status >= 0); |
|
|
|
|
int stderr_producer_fd = -1; |
|
|
|
|
zx_handle_t producer_handle[1] = { stderr_producer_socket.release() }; |
|
|
|
|
uint32_t producer_handle_type[1] = { PA_FDIO_SOCKET }; |
|
|
|
|
status = fdio_create_fd( |
|
|
|
|
producer_handle, producer_handle_type, 1, &stderr_producer_fd); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status >= 0); |
|
|
|
|
|
|
|
|
|
// Make the stderr socket nonblocking.
|
|
|
|
|
GTEST_DEATH_TEST_CHECK_(fcntl(stderr_producer_fd, F_SETFL, 0) == 0); |
|
|
|
|
|
|
|
|
|
fdio_spawn_action_t* add_stderr_action = &spawn_actions[1]; |
|
|
|
|
add_stderr_action->action = FDIO_SPAWN_ACTION_CLONE_FD; |
|
|
|
|
add_stderr_action->fd.local_fd = stderr_producer_fd; |
|
|
|
|
add_stderr_action->fd.target_fd = STDERR_FILENO; |
|
|
|
|
|
|
|
|
|
// Spawn the child process.
|
|
|
|
|
status = fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, |
|
|
|
|
args.Argv()[0], args.Argv(), nullptr, 1, |
|
|
|
|
&add_handle_action, &child_process_, nullptr); |
|
|
|
|
status = fdio_spawn_etc( |
|
|
|
|
ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, args.Argv()[0], args.Argv(), |
|
|
|
|
nullptr, 2, spawn_actions, child_process_.reset_and_get_address(), |
|
|
|
|
nullptr); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status == ZX_OK); |
|
|
|
|
|
|
|
|
|
// Create an exception port and attach it to the |child_process_|, to allow
|
|
|
|
|
// us to suppress the system default exception handler from firing.
|
|
|
|
|
status = zx_port_create(0, &port_); |
|
|
|
|
status = zx::port::create(0, &port_); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status == ZX_OK); |
|
|
|
|
status = zx_task_bind_exception_port( |
|
|
|
|
child_process_, port_, 0 /* key */, 0 /*options */); |
|
|
|
|
status = child_process_.bind_exception_port( |
|
|
|
|
port_, 0 /* key */, 0 /*options */); |
|
|
|
|
GTEST_DEATH_TEST_CHECK_(status == ZX_OK); |
|
|
|
|
|
|
|
|
|
set_spawned(true); |
|
|
|
|
return OVERSEE_TEST; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string FuchsiaDeathTest::GetErrorLogs() { |
|
|
|
|
return captured_stderr_; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#else // We are neither on Windows, nor on Fuchsia.
|
|
|
|
|
|
|
|
|
|
// ForkingDeathTest provides implementations for most of the abstract
|
|
|
|
|