[lib/debugger_utils] Add test fixture for running subprograms
Change-Id: I9069b2f9d177c30dd08cb00b109e020f250d4c70
diff --git a/garnet/lib/debugger_utils/BUILD.gn b/garnet/lib/debugger_utils/BUILD.gn
index f0097f0..a9ea860 100644
--- a/garnet/lib/debugger_utils/BUILD.gn
+++ b/garnet/lib/debugger_utils/BUILD.gn
@@ -86,6 +86,8 @@
"run_all_unittests.cc",
"sysinfo.cc",
"sysinfo.h",
+ "test_helper_fixture.cc",
+ "test_helper_fixture.h",
"threads.cc",
"threads.h",
"threads_unittest.cc",
diff --git a/garnet/lib/debugger_utils/jobs_unittest.cc b/garnet/lib/debugger_utils/jobs_unittest.cc
index 96d020f..2b760ce 100644
--- a/garnet/lib/debugger_utils/jobs_unittest.cc
+++ b/garnet/lib/debugger_utils/jobs_unittest.cc
@@ -2,16 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <lib/fdio/spawn.h>
-#include <lib/fxl/arraysize.h>
-#include <lib/zx/channel.h>
-#include <zircon/processargs.h>
+#include <gtest/gtest.h>
#include "garnet/lib/debugger_utils/jobs.h"
#include "garnet/lib/debugger_utils/sysinfo.h"
#include "garnet/lib/debugger_utils/test_helper.h"
+#include "garnet/lib/debugger_utils/test_helper_fixture.h"
#include "garnet/lib/debugger_utils/util.h"
-#include "gtest/gtest.h"
namespace debugger_utils {
namespace {
@@ -130,84 +127,48 @@
EXPECT_EQ(process_depth + 1, thread_depth);
}
-static void BuildTestProcess(const zx::job& parent_job, zx::job* job,
- zx::process* process, zx::thread* thread,
- zx::channel* channel) {
- ASSERT_EQ(zx::job::create(parent_job, 0, job), ZX_OK);
+TEST_F(TestWithHelper, JobsTestChildJob) {
+ auto parent_job = GetDefaultJob();
+ zx::job child_job;
+ zx::thread thread;
- const char* argv[] = {
- kTestHelperPath,
- nullptr,
- };
+ ASSERT_EQ(zx::job::create(parent_job, 0, &child_job), ZX_OK);
+ ASSERT_EQ(RunHelperProgram(child_job, kWaitPeerClosedArgv), ZX_OK);
// We need the handle of the main thread of the process for test purposes.
// Technically, we only need its koid. HOWEVER, we do not obtain the koid
// via, say, ZX_INFO_PROCESS_THREADS, because that is used by the routine
// we are testing: |WalkJobTree()|. Try to KISS and just get the thread's
// handle. Alas it's not that simple.
- zx::channel their_channel;
- ASSERT_EQ(zx::channel::create(0, channel, &their_channel), ZX_OK);
- fdio_spawn_action actions[1];
- actions[0].action = FDIO_SPAWN_ACTION_ADD_HANDLE;
- actions[0].h.id = PA_HND(PA_USER0, 0);
- actions[0].h.handle = their_channel.release();
- uint32_t flags = FDIO_SPAWN_CLONE_ALL;
- char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
+ ASSERT_EQ(GetHelperThread(&thread), ZX_OK);
- zx_status_t status = fdio_spawn_etc(job->get(), flags, kTestHelperPath, argv,
- nullptr, arraysize(actions), actions,
- process->reset_and_get_address(),
- err_msg);
- ASSERT_EQ(status, ZX_OK) << err_msg;
-
- zx_signals_t pending;
- ASSERT_EQ(channel->wait_one(ZX_CHANNEL_READABLE, zx::time::infinite(),
- &pending), ZX_OK);
-
- uint32_t actual_bytes, actual_handles;
- ASSERT_EQ(channel->read(0u, nullptr, 0u,
- &actual_bytes, thread->reset_and_get_address(),
- 1u, &actual_handles), ZX_OK);
- EXPECT_EQ(actual_bytes, 0u);
- EXPECT_EQ(actual_handles, 1u);
-
- // At this point the inferior is waiting for us to close the channel.
-}
-
-TEST(JobsTest, ChildJob) {
- auto parent_job = GetDefaultJob();
- zx::job child_job;
- zx::process process;
- zx::thread thread;
- zx::channel channel;
-
- BuildTestProcess(parent_job, &child_job, &process, &thread, &channel);
ASSERT_TRUE(child_job.is_valid());
- ASSERT_TRUE(process.is_valid());
+ ASSERT_TRUE(process().is_valid());
ASSERT_TRUE(thread.is_valid());
- ASSERT_TRUE(channel.is_valid());
-
- TestDepth(parent_job, child_job.get(), process.get(), thread.get());
+ ASSERT_TRUE(channel().is_valid());
+
+ TestDepth(parent_job, child_job.get(), process().get(), thread.get());
}
-TEST(JobsTest, RootJob) {
+TEST_F(TestWithHelper, JobsTestRootJob) {
// Make sure we can find ourselves from the root job.
// This will likely evolve or be replaced, but it's useful to test
// current functionality.
auto search_job = GetRootJob();
auto parent_job = GetDefaultJob();
zx::job child_job;
- zx::process process;
zx::thread thread;
- zx::channel channel;
- BuildTestProcess(parent_job, &child_job, &process, &thread, &channel);
+ ASSERT_EQ(zx::job::create(parent_job, 0, &child_job), ZX_OK);
+ ASSERT_EQ(RunHelperProgram(child_job, kWaitPeerClosedArgv), ZX_OK);
+ ASSERT_EQ(GetHelperThread(&thread), ZX_OK);
+
ASSERT_TRUE(child_job.is_valid());
- ASSERT_TRUE(process.is_valid());
+ ASSERT_TRUE(process().is_valid());
ASSERT_TRUE(thread.is_valid());
- ASSERT_TRUE(channel.is_valid());
+ ASSERT_TRUE(channel().is_valid());
- TestDepth(search_job, child_job.get(), process.get(), thread.get());
+ TestDepth(search_job, child_job.get(), process().get(), thread.get());
}
TEST(JobsTest, FindProcess) {
diff --git a/garnet/lib/debugger_utils/test_helper_fixture.cc b/garnet/lib/debugger_utils/test_helper_fixture.cc
new file mode 100644
index 0000000..1987d0e
--- /dev/null
+++ b/garnet/lib/debugger_utils/test_helper_fixture.cc
@@ -0,0 +1,91 @@
+// Copyright 2019 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 <lib/fdio/spawn.h>
+#include <lib/fxl/arraysize.h>
+#include <lib/fxl/logging.h>
+#include <zircon/processargs.h>
+
+#include "test_helper.h"
+#include "test_helper_fixture.h"
+#include "util.h"
+
+namespace debugger_utils {
+
+const char* const TestWithHelper::kWaitPeerClosedArgv[] = {
+ kTestHelperPath,
+ "wait-peer-closed",
+ nullptr,
+};
+
+void TestWithHelper::SetUp() {
+}
+
+void TestWithHelper::TearDown() {
+ // Closing the channel should cause the helper to terminate, if it
+ // hasn't already.
+ channel_.reset();
+
+ zx_signals_t pending;
+ zx_status_t status =
+ process_.wait_one(ZX_PROCESS_TERMINATED, zx::time::infinite(), &pending);
+ ASSERT_EQ(status, ZX_OK);
+}
+
+zx_status_t TestWithHelper::RunHelperProgram(const zx::job& job,
+ const char* const argv[]) {
+ zx::channel our_channel, their_channel;
+ zx_status_t status = zx::channel::create(0, &our_channel, &their_channel);
+ if (status != ZX_OK) {
+ FXL_LOG(ERROR) << "zx::channel::create failed: " << ZxErrorString(status);
+ return status;
+ }
+ fdio_spawn_action actions[1];
+ actions[0].action = FDIO_SPAWN_ACTION_ADD_HANDLE;
+ actions[0].h.id = PA_HND(PA_USER0, 0);
+ actions[0].h.handle = their_channel.release();
+ uint32_t flags = FDIO_SPAWN_CLONE_ALL;
+ char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
+
+ zx::process process;
+ status = fdio_spawn_etc(job.get(), flags, kTestHelperPath, argv, nullptr,
+ arraysize(actions), actions,
+ process.reset_and_get_address(), err_msg);
+ if (status != ZX_OK) {
+ FXL_LOG(ERROR) << "fdio_spawn_etc failed: " << ZxErrorString(status)
+ << ", " << err_msg;
+ return status;
+ }
+
+ process_ = std::move(process);
+ channel_ = std::move(our_channel);
+ return status;
+}
+
+zx_status_t TestWithHelper::GetHelperThread(zx::thread* out_thread) {
+ zx_signals_t pending;
+ zx_status_t status =
+ channel_.wait_one(ZX_CHANNEL_READABLE, zx::time::infinite(), &pending);
+ if (status != ZX_OK) {
+ FXL_LOG(ERROR) << "channel->wait_one failed: " << ZxErrorString(status);
+ return status;
+ }
+
+ uint32_t actual_bytes, actual_handles;
+ status = channel_.read(0u, nullptr, 0u,
+ &actual_bytes, out_thread->reset_and_get_address(),
+ 1u, &actual_handles);
+ if (status != ZX_OK) {
+ FXL_LOG(ERROR) << "channel->read failed: " << ZxErrorString(status);
+ return status;
+ }
+ EXPECT_EQ(actual_bytes, 0u);
+ EXPECT_EQ(actual_handles, 1u);
+
+ // At this point the inferior is generally waiting for us to close the
+ // channel.
+ return ZX_OK;
+}
+
+} // namespace debugger_utils
diff --git a/garnet/lib/debugger_utils/test_helper_fixture.h b/garnet/lib/debugger_utils/test_helper_fixture.h
new file mode 100644
index 0000000..25ddd98
--- /dev/null
+++ b/garnet/lib/debugger_utils/test_helper_fixture.h
@@ -0,0 +1,44 @@
+// Copyright 2019 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.
+
+#ifndef GARNET_LIB_DEBUGGER_UTILS_TEST_HELPER_FIXTURE_H_
+#define GARNET_LIB_DEBUGGER_UTILS_TEST_HELPER_FIXTURE_H_
+
+#include <gtest/gtest.h>
+#include <lib/zx/channel.h>
+#include <lib/zx/job.h>
+#include <lib/zx/process.h>
+#include <lib/zx/thread.h>
+
+namespace debugger_utils {
+
+class TestWithHelper : public ::testing::Test {
+ public:
+ // Pass this for |argv| to have the inferior send back a handle to its
+ // main thread and then wait for us to close the channel.
+ static const char* const kWaitPeerClosedArgv[];
+
+ // ::testing::Test overrides
+ void SetUp() override;
+ void TearDown() override;
+
+ // Call this to run the helper program with |argv| under |job|.
+ zx_status_t RunHelperProgram(const zx::job& job, const char* const argv[]);
+
+ // Call this after |RunHelperProgram| to obtain the handle of the main
+ // thread in the helper program. This assumes the helper program is
+ // following the necessary protocol to send the handle.
+ zx_status_t GetHelperThread(zx::thread* out_thread);
+
+ const zx::process& process() const { return process_; }
+ const zx::channel& channel() const { return channel_; }
+
+ private:
+ zx::process process_;
+ zx::channel channel_;
+};
+
+} // namespace debugger_utils
+
+#endif // GARNET_LIB_DEBUGGER_UTILS_TEST_HELPER_FIXTURE_H_
diff --git a/garnet/lib/debugger_utils/util_zx_unittest.cc b/garnet/lib/debugger_utils/util_zx_unittest.cc
index 627b8c1..b1a1f3c 100644
--- a/garnet/lib/debugger_utils/util_zx_unittest.cc
+++ b/garnet/lib/debugger_utils/util_zx_unittest.cc
@@ -32,7 +32,7 @@
zx::process process;
zx::job job{GetDefaultJob()};
- const char* argv[] = {
+ static const char* const argv[] = {
kTestHelperPath,
nullptr,
};