blob: 96f05766fcf799ec43ae44e2ec0458851375e73d [file] [log] [blame]
// 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 "src/developer/debug/zxdb/console/verbs_settings.h"
#include <gtest/gtest.h>
#include "src/developer/debug/shared/platform_message_loop.h"
#include "src/developer/debug/zxdb/client/execution_scope.h"
#include "src/developer/debug/zxdb/client/mock_remote_api.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/developer/debug/zxdb/client/remote_api_test.h"
#include "src/developer/debug/zxdb/console/console_context.h"
#include "src/developer/debug/zxdb/console/mock_console.h"
#include "src/developer/debug/zxdb/symbols/loaded_module_symbols.h"
#include "src/developer/debug/zxdb/symbols/process_symbols.h"
namespace zxdb {
namespace {
// TODO(brettw) Convert to a ConsoleTest to remove some boilerplate.
class VerbsSettingsTest : public RemoteAPITest {
public:
VerbsSettingsTest() : RemoteAPITest() {}
// The MockConsole must live in a smaller scope than the Session/System managed by the
// RemoteAPITest's SetUp()/TearDown() functions.
void SetUp() override {
RemoteAPITest::SetUp();
console_ = std::make_unique<MockConsole>(&session());
}
void TearDown() override {
console_.reset();
RemoteAPITest::TearDown();
}
MockConsole& console() { return *console_; }
// Runs an input command and returns the text synchronously reported from it.
std::string DoInput(const std::string& input) {
console_->ProcessInputLine(input);
auto event = console_->GetOutputEvent();
EXPECT_EQ(MockConsole::OutputEvent::Type::kOutput, event.type);
return event.output.AsString();
}
private:
std::unique_ptr<MockConsole> console_;
};
} // namespace
TEST(VerbsSettings, ParseSetCommand) {
// No input.
ErrOr<ParsedSetCommand> result = ParseSetCommand("");
ASSERT_TRUE(result.has_error());
EXPECT_EQ("Expected a setting name to set.", result.err().msg());
// Missing value.
result = ParseSetCommand("foo");
ASSERT_TRUE(result.has_error());
EXPECT_EQ("Expecting a value to set. Use \"set setting-name setting-value\".",
result.err().msg());
// Regular two-argument form.
result = ParseSetCommand("foo bar");
ASSERT_TRUE(result.ok());
EXPECT_EQ("foo", result.value().name);
EXPECT_EQ(ParsedSetCommand::kAssign, result.value().op);
EXPECT_EQ("bar", result.value().raw_value);
ASSERT_EQ(1u, result.value().values.size());
EXPECT_EQ("bar", result.value().values[0]);
// Value ending in hyphen.
result = ParseSetCommand("foo-");
ASSERT_TRUE(result.has_error());
EXPECT_EQ("Invalid setting name.", result.err().msg());
result = ParseSetCommand("foo- bar");
ASSERT_TRUE(result.has_error());
EXPECT_EQ("Invalid setting name.", result.err().msg());
// Whitespace or an assignment operator is required after the name.
result = ParseSetCommand("foo#bar");
ASSERT_FALSE(result.ok());
EXPECT_EQ("Invalid setting name.", result.err().msg());
// Regular three-argument form with spaces.
result = ParseSetCommand("foo = bar ");
ASSERT_TRUE(result.ok());
EXPECT_EQ("foo", result.value().name);
EXPECT_EQ(ParsedSetCommand::kAssign, result.value().op);
EXPECT_EQ("bar", result.value().raw_value);
ASSERT_EQ(1u, result.value().values.size());
EXPECT_EQ("bar", result.value().values[0]);
// Regular three-argument form with no spaces and an "append" mode.
result = ParseSetCommand("foo+=bar");
ASSERT_TRUE(result.ok());
EXPECT_EQ("foo", result.value().name);
EXPECT_EQ(ParsedSetCommand::kAppend, result.value().op);
ASSERT_EQ(1u, result.value().values.size());
EXPECT_EQ("bar", result.value().values[0]);
// Remove mode with value quoting.
result = ParseSetCommand("foo-=\"bar baz\"");
ASSERT_TRUE(result.ok());
EXPECT_EQ("foo", result.value().name);
EXPECT_EQ(ParsedSetCommand::kRemove, result.value().op);
EXPECT_EQ("\"bar baz\"", result.value().raw_value);
ASSERT_EQ(1u, result.value().values.size());
EXPECT_EQ("bar baz", result.value().values[0]);
// Assign many values.
result = ParseSetCommand("foo bar baz");
ASSERT_TRUE(result.ok());
EXPECT_EQ("foo", result.value().name);
EXPECT_EQ(ParsedSetCommand::kAssign, result.value().op);
EXPECT_EQ("bar baz", result.value().raw_value);
ASSERT_EQ(2u, result.value().values.size());
EXPECT_EQ("bar", result.value().values[0]);
EXPECT_EQ("baz", result.value().values[1]);
result = ParseSetCommand("foo+=bar \"baz goo\"");
ASSERT_TRUE(result.ok());
EXPECT_EQ("foo", result.value().name);
EXPECT_EQ(ParsedSetCommand::kAppend, result.value().op);
EXPECT_EQ("bar \"baz goo\"", result.value().raw_value);
ASSERT_EQ(2u, result.value().values.size());
EXPECT_EQ("bar", result.value().values[0]);
EXPECT_EQ("baz goo", result.value().values[1]);
}
TEST_F(VerbsSettingsTest, GetSet) {
console().FlushOutputEvents();
// "get" with no input.
EXPECT_EQ("", DoInput("get --value-only build-dirs"));
// "get" with an invalid object (there is no active filter).
EXPECT_EQ("No current object of this type.", DoInput("filter get --value-only"));
// Process qualified set.
EXPECT_EQ(
"Set process 1 build-dirs = \n"
" • prdir\n",
DoInput("pr set build-dirs prdir"));
// Both the unqualified and process-qualified one should get it,
EXPECT_EQ("prdir", DoInput("get --value-only build-dirs"));
EXPECT_EQ("prdir", DoInput("process get --value-only build-dirs"));
// Globally qualified set.
EXPECT_EQ(
"Set global build-dirs = \n"
" • gldir\n",
DoInput("global set build-dirs gldir"));
// The globally qualified one should return it, but the unqualified one should return the process
// since it's more specific.
EXPECT_EQ("prdir", DoInput("process get --value-only build-dirs"));
EXPECT_EQ("gldir", DoInput("global get --value-only build-dirs"));
EXPECT_EQ("prdir", DoInput("get --value-only build-dirs"));
// Unqualified set.
EXPECT_EQ(
"Set global build-dirs = \n"
" • gldir2\n",
DoInput("set build-dirs gldir2"));
// Append.
EXPECT_EQ(
"Set global build-dirs = \n"
" • gldir2\n"
" • gldir3\n"
" • \"gldir four\"\n",
DoInput("set build-dirs += gldir3 \"gldir four\""));
// Create a breakpoint and test scope assignment (this doesn't need to be quoted).
DoInput("break main");
EXPECT_EQ("Set breakpoint 1 scope = pr 1\n", DoInput("bp 1 set scope pr 1"));
// There is no current thread so setting the thread scope should fail.
EXPECT_EQ("There are no threads in the process.", DoInput("bp 1 set scope thread 1"));
EXPECT_EQ("There is no current thread to use for the scope.", DoInput("bp 1 set scope thread"));
EXPECT_EQ("gldir2 gldir3 \"gldir four\"", DoInput("global get --value-only build-dirs"));
EXPECT_EQ("prdir", DoInput("get --value-only build-dirs"));
// Check invalid values.
EXPECT_EQ("Could not find setting \"unknown-setting\".", DoInput("set unknown-setting = blah"));
EXPECT_EQ("Could not find setting \"unknown-setting\".", DoInput("get unknown-setting"));
}
TEST_F(VerbsSettingsTest, ParseExecutionScope) {
ConsoleContext context(&session());
// Inject one running process and thread.
constexpr int kProcessKoid = 1234;
InjectProcess(kProcessKoid);
InjectThread(kProcessKoid, 5678);
// Random input.
ErrOr<ExecutionScope> result = ParseExecutionScope(&context, "something invalid");
EXPECT_TRUE(result.has_error());
// Valid nouns but not the right ones.
result = ParseExecutionScope(&context, "breakpoint 3");
EXPECT_TRUE(result.has_error());
// Verb (not valid).
result = ParseExecutionScope(&context, "process next");
EXPECT_TRUE(result.has_error());
// Valid global scope.
result = ParseExecutionScope(&context, "global");
EXPECT_TRUE(result.ok());
EXPECT_EQ(ExecutionScope::kSystem, result.value().type());
// Valid process scope.
result = ParseExecutionScope(&context, "process");
ASSERT_TRUE(result.ok());
EXPECT_EQ(ExecutionScope::kTarget, result.value().type());
result = ParseExecutionScope(&context, "process 1");
ASSERT_TRUE(result.ok());
EXPECT_EQ(ExecutionScope::kTarget, result.value().type());
// Invalid process scope (there's only one).
result = ParseExecutionScope(&context, "process 2");
EXPECT_TRUE(result.has_error());
// Valid thread scope.
result = ParseExecutionScope(&context, "thread");
ASSERT_TRUE(result.ok());
EXPECT_EQ(ExecutionScope::kThread, result.value().type());
result = ParseExecutionScope(&context, "process 1 thread 1");
ASSERT_TRUE(result.ok());
EXPECT_EQ(ExecutionScope::kThread, result.value().type());
// Invalid process scope (there's only one).
result = ParseExecutionScope(&context, "thread 2");
EXPECT_TRUE(result.has_error());
}
} // namespace zxdb