blob: 55a54a44701a6d0a726ebc309b259914053561f6 [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_definitions.h"
#include "src/developer/debug/zxdb/client/mock_remote_api.h"
#include "src/developer/debug/zxdb/console/commands/breakpoint_command_test.h"
#include "src/developer/debug/zxdb/console/console_test.h"
namespace zxdb {
namespace {
class VerbBreak : public BreakpointCommandTest {};
} // namespace
TEST_F(VerbBreak, Break) {
// Process starts out as running. Make an expression breakpoint.
console().ProcessInputLine("break *0x1230 + 4");
// Validate the set request.
ASSERT_TRUE(breakpoint_remote_api()->last_request);
ASSERT_EQ(1u, breakpoint_remote_api()->last_request->breakpoint.locations.size());
EXPECT_EQ(0x1234u, breakpoint_remote_api()->last_request->breakpoint.locations[0].address);
// The breakpoint info should be immediately printed even though the backend has not replied.
auto event = console().GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ("Created Breakpoint 1 @ 0x1234\n", event.output.AsString());
// Issue the success callback from the backend. Nothing should be printed.
ASSERT_TRUE(breakpoint_remote_api()->last_cb);
breakpoint_remote_api()->last_cb(Err(), debug_ipc::AddOrChangeBreakpointReply());
EXPECT_FALSE(console().HasOutputEvent());
// Make a new process that's not running and then a breakpoint.
console().ProcessInputLine("process new");
console().FlushOutputEvents();
console().ProcessInputLine("break SomePendingFunc");
// It should give a pending message.
event = console().GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ(
"Created Breakpoint 2 @ SomePendingFunc\n"
"Pending: No current matches for location. It will be matched against new\n"
" processes and shared libraries.\n",
event.output.AsString());
}
TEST_F(VerbBreak, WriteBreakpoint) {
// Creates a specifically-sized write breakpoint at a manual address.
console().ProcessInputLine("break -t write *(uint16_t*)0x1234");
// Validate the set request. It will have a 0 address but the range will be set to cover the
// 16-bit input value.
ASSERT_TRUE(breakpoint_remote_api()->last_request);
ASSERT_EQ(1u, breakpoint_remote_api()->last_request->breakpoint.locations.size());
EXPECT_EQ(0u, breakpoint_remote_api()->last_request->breakpoint.locations[0].address);
EXPECT_EQ(0x1234u,
breakpoint_remote_api()->last_request->breakpoint.locations[0].address_range.begin());
EXPECT_EQ(2u,
breakpoint_remote_api()->last_request->breakpoint.locations[0].address_range.size());
// The breakpoint info should be immediately printed even though the backend has not replied.
auto event = console().GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ("Created Breakpoint 1 type=write size=2 @ 0x1234\n", event.output.AsString());
// Now do an explicitly-sized override.
console().ProcessInputLine("break -s 8 -t read-write *(uint16_t*)0x5678");
ASSERT_TRUE(breakpoint_remote_api()->last_request);
EXPECT_EQ(0u, breakpoint_remote_api()->last_request->breakpoint.locations[0].address);
EXPECT_EQ(0x5678u,
breakpoint_remote_api()->last_request->breakpoint.locations[0].address_range.begin());
EXPECT_EQ(8u,
breakpoint_remote_api()->last_request->breakpoint.locations[0].address_range.size());
// The breakpoint info should be immediately printed even though the backend has not replied.
event = console().GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ("Created Breakpoint 2 type=read-write size=8 @ 0x5678\n", event.output.AsString());
// Untyped numeric inputs get a default size of 4.
console().ProcessInputLine("break -t read-write 0x9abc");
ASSERT_TRUE(breakpoint_remote_api()->last_request);
EXPECT_EQ(0u, breakpoint_remote_api()->last_request->breakpoint.locations[0].address);
EXPECT_EQ(0x9abcu,
breakpoint_remote_api()->last_request->breakpoint.locations[0].address_range.begin());
EXPECT_EQ(4u,
breakpoint_remote_api()->last_request->breakpoint.locations[0].address_range.size());
}
TEST_F(VerbBreak, ExpressionError) {
// This variable is not found.
console().ProcessInputLine("break -t write *&nonesitant_var");
// It might be nice if the error message mentioned the "break" command but the verb name isn't
// plumbed through to the error message currently so this is a generic "the command" error.
auto event = console().GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ(
"Unable to evaluate the expression for the command. The result was:\n"
" No variable 'nonesitant_var' found.",
event.output.AsString());
// No breakpoint should have been created.
EXPECT_FALSE(breakpoint_remote_api()->last_request);
}
// This is a more end-to-end-type test that tests that breakpoints that hit backend errors issue
// the proper notification and those notifications are caught and printed out on the screen.
TEST_F(VerbBreak, TransportError) {
// Create a breakpoint.
console().ProcessInputLine("break 0x1234");
auto event = console().GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ("Created Breakpoint 1 @ 0x1234\n", event.output.AsString());
// Issue the callback with a transport error.
ASSERT_TRUE(breakpoint_remote_api()->last_cb);
breakpoint_remote_api()->last_cb(Err("Some transport error."),
debug_ipc::AddOrChangeBreakpointReply());
// The ConsoleContext should have printed out the error.
event = console().GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ(
"Error updating Breakpoint 1 @ 0x1234\n"
"Some transport error.",
event.output.AsString());
}
TEST_F(VerbBreak, BackendError) {
// Create a breakpoint.
console().ProcessInputLine("break 0x2345");
auto event = console().GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ("Created Breakpoint 1 @ 0x2345\n", event.output.AsString());
// Issue the callback with a backend error.
ASSERT_TRUE(breakpoint_remote_api()->last_cb);
debug_ipc::AddOrChangeBreakpointReply reply;
reply.status = debug::Status("Bad handle");
breakpoint_remote_api()->last_cb(Err(), reply);
// The ConsoleContext should have printed out the error.
event = console().GetOutputEvent();
ASSERT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
EXPECT_EQ(
"Error updating Breakpoint 1 @ 0x2345\n"
"Bad handle",
event.output.AsString());
}
} // namespace zxdb