blob: 08f2960b4ee7e6c555c5d0408d79ad607d4bbda5 [file] [log] [blame]
// Copyright 2020 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 <gtest/gtest.h>
#include "src/developer/debug/shared/zx_status.h"
#include "src/developer/debug/zxdb/client/mock_remote_api.h"
#include "src/developer/debug/zxdb/client/target_impl.h"
#include "src/developer/debug/zxdb/debug_adapter/context_test.h"
namespace zxdb {
TEST_F(DebugAdapterContextTest, InitializeRequest) {
// Send initialize request from the client.
auto response = client().send(dap::InitializeRequest{});
// Read request and process it.
context().OnStreamReadable();
// Run client to receive response.
RunClient();
auto got = response.get();
EXPECT_EQ(got.error, false);
EXPECT_EQ(bool(got.response.supportsFunctionBreakpoints), true);
EXPECT_EQ(bool(got.response.supportsConfigurationDoneRequest), true);
}
TEST_F(DebugAdapterContextTest, InitializedEvent) {
bool event_received = false;
client().registerHandler([&](const dap::InitializedEvent& arg) { event_received = true; });
// Send initialize request from the client.
auto response = client().send(dap::InitializeRequest{});
context().OnStreamReadable();
// Run client twice to receive response and event.
RunClient();
RunClient();
EXPECT_TRUE(event_received);
}
TEST_F(DebugAdapterContextTest, ProcessStartEvent) {
bool start_received = false;
client().registerHandler([&](const dap::ProcessEvent& arg) { start_received = true; });
InitializeDebugging();
InjectProcess(kProcessKoid);
// Receive Process started event in client.
RunClient();
EXPECT_TRUE(start_received);
}
TEST_F(DebugAdapterContextTest, ThreadStartExitEvent) {
bool start_received = false;
bool exit_received = false;
client().registerHandler([&](const dap::ThreadEvent& arg) {
EXPECT_EQ(arg.threadId, static_cast<dap::integer>(kThreadKoid));
if (arg.reason == "started") {
start_received = true;
}
if (arg.reason == "exited") {
exit_received = true;
}
});
InitializeDebugging();
InjectProcess(kProcessKoid);
// Receive process started event in client.
RunClient();
InjectThread(kProcessKoid, kThreadKoid);
// Receive thread started event in client.
RunClient();
EXPECT_TRUE(start_received);
debug_ipc::NotifyThread notify;
notify.record.id = {.process = kProcessKoid, .thread = kThreadKoid};
notify.record.state = debug_ipc::ThreadRecord::State::kDying;
session().DispatchNotifyThreadExiting(notify);
// Receive thread exited event in client.
RunClient();
EXPECT_TRUE(exit_received);
}
TEST_F(DebugAdapterContextTest, StoppedEvent) {
bool event_received = false;
client().registerHandler([&](const dap::StoppedEvent& arg) {
EXPECT_EQ(arg.reason, "breakpoint");
EXPECT_TRUE(arg.threadId.has_value());
EXPECT_EQ(arg.threadId.value(), static_cast<dap::integer>(kThreadKoid));
event_received = true;
});
InitializeDebugging();
InjectProcess(kProcessKoid);
// Receive process started event in client.
RunClient();
InjectThread(kProcessKoid, kThreadKoid);
// Receive thread started event in client.
RunClient();
constexpr uint64_t kAddress = 0x12345678;
constexpr uint64_t kStack = 0x7890;
// Notify of thread stop.
debug_ipc::NotifyException break_notification;
break_notification.type = debug_ipc::ExceptionType::kSoftwareBreakpoint;
break_notification.thread.id = {.process = kProcessKoid, .thread = kThreadKoid};
break_notification.thread.state = debug_ipc::ThreadRecord::State::kBlocked;
break_notification.thread.frames.emplace_back(kAddress, kStack, kStack);
InjectException(break_notification);
// Receive thread stopped event in client.
RunClient();
EXPECT_TRUE(event_received);
}
TEST_F(DebugAdapterContextTest, DisconnectRequest) {
bool request_received = false;
context().set_destroy_connection_callback([&request_received]() { request_received = true; });
InitializeDebugging();
// Send disconnect request from client
auto response = client().send(dap::DisconnectRequest());
// Receive and process request in the server.
context().OnStreamReadable();
loop().RunUntilNoTasks();
// Run client to receive response.
RunClient();
auto got = response.get();
EXPECT_FALSE(got.error);
EXPECT_TRUE(request_received);
}
TEST_F(DebugAdapterContextTest, ExitedEvent) {
bool event_received = false;
client().registerHandler([&event_received](const dap::ExitedEvent& arg) {
EXPECT_EQ(arg.exitCode, 20);
event_received = true;
});
InitializeDebugging();
InjectProcess(kProcessKoid);
// Receive process started event in client.
RunClient();
InjectThread(kProcessKoid, kThreadKoid);
// Receive thread started event in client.
RunClient();
// Detach from target
auto targets = session().system().GetTargetImpls();
ASSERT_EQ(targets.size(), 1u);
targets[0]->OnProcessExiting(20, 0);
// Run client to receive event.
RunClient();
EXPECT_TRUE(event_received);
}
namespace {
class ProcessDetachRemoteAPI : public MockRemoteAPI {
public:
void Detach(const debug_ipc::DetachRequest& request,
fit::callback<void(const Err&, debug_ipc::DetachReply)> cb) override {
debug_ipc::DetachReply reply;
cb(Err(), reply);
}
};
class ProcessDetachTest : public DebugAdapterContextTest {
public:
ProcessDetachRemoteAPI* remote_api() const { return remote_api_; }
protected:
std::unique_ptr<RemoteAPI> GetRemoteAPIImpl() override {
auto remote_api = std::make_unique<ProcessDetachRemoteAPI>();
remote_api_ = remote_api.get();
return remote_api;
}
private:
ProcessDetachRemoteAPI* remote_api_;
};
} // namespace
TEST_F(ProcessDetachTest, TerminatedEvent) {
bool event_received = false;
client().registerHandler(
[&event_received](const dap::TerminatedEvent& arg) { event_received = true; });
InitializeDebugging();
InjectProcess(kProcessKoid);
// Receive process started event in client.
RunClient();
InjectThread(kProcessKoid, kThreadKoid);
// Receive thread started event in client.
RunClient();
// Detach from target
auto targets = session().system().GetTargetImpls();
ASSERT_EQ(targets.size(), 1u);
targets[0]->Detach([](const fxl::WeakPtr<Target>& target, const Err& err) {});
// Run client to receive event.
RunClient();
EXPECT_TRUE(event_received);
}
} // namespace zxdb