blob: 78b239730e849c6e3c876ce4b8f11533f401dbc6 [file] [log] [blame]
// Copyright 2018 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 "garnet/bin/zxdb/console/command_utils.h"
#include <inttypes.h>
#include <limits>
#include "garnet/bin/zxdb/common/err.h"
#include "garnet/bin/zxdb/console/command.h"
#include "garnet/bin/zxdb/console/output_buffer.h"
#include "garnet/bin/zxdb/symbols/base_type.h"
#include "garnet/bin/zxdb/symbols/function.h"
#include "garnet/bin/zxdb/symbols/location.h"
#include "garnet/bin/zxdb/symbols/namespace.h"
#include "garnet/bin/zxdb/symbols/type_test_support.h"
#include "garnet/bin/zxdb/symbols/variable_test_support.h"
#include "gtest/gtest.h"
#include "lib/fxl/strings/string_printf.h"
namespace zxdb {
TEST(CommandUtils, StringToInt) {
// Leading 0's not octal.
int result = 0;
EXPECT_FALSE(StringToInt("010", &result).has_error());
EXPECT_EQ(10, result);
// Negative hexadecimal.
EXPECT_FALSE(StringToInt("-0x1a", &result).has_error());
EXPECT_EQ(-0x1a, result);
// Trimmed
EXPECT_FALSE(StringToInt(" -0x1a ", &result).has_error());
EXPECT_EQ(-0x1a, result);
// Test at the limits.
constexpr int kMax = std::numeric_limits<int>::max();
EXPECT_FALSE(StringToInt(fxl::StringPrintf("%d", kMax), &result).has_error());
EXPECT_EQ(kMax, result);
constexpr int kMin = std::numeric_limits<int>::lowest();
EXPECT_FALSE(StringToInt(fxl::StringPrintf("%d", kMin), &result).has_error());
EXPECT_EQ(kMin, result);
// Test just beyond the limits.
int64_t kBeyondMax = static_cast<int64_t>(kMax) + 1;
EXPECT_TRUE(StringToInt(fxl::StringPrintf("%" PRId64, kBeyondMax), &result)
.has_error());
int64_t kBeyondMin = static_cast<int64_t>(kMin) - 1;
EXPECT_TRUE(StringToInt(fxl::StringPrintf("%" PRId64, kBeyondMin), &result)
.has_error());
}
TEST(CommandUtils, StringToUint32) {
uint32_t result = 0;
EXPECT_FALSE(StringToUint32("032", &result).has_error());
EXPECT_EQ(32u, result);
// Test at and just beyond the limits.
EXPECT_FALSE(StringToUint32("0xffffffff", &result).has_error());
EXPECT_EQ(0xffffffff, result);
EXPECT_TRUE(StringToUint32("0x100000000", &result).has_error());
// Trimming
EXPECT_FALSE(StringToUint32(" 0xffffffff ", &result).has_error());
EXPECT_EQ(0xffffffff, result);
}
TEST(CommandUtils, StringToUint64) {
uint64_t result = 0;
EXPECT_FALSE(StringToUint64("1234", &result).has_error());
EXPECT_EQ(1234u, result);
// Empty string.
EXPECT_TRUE(StringToUint64("", &result).has_error());
// Non-numbers.
EXPECT_TRUE(StringToUint64("asdf", &result).has_error());
EXPECT_TRUE(StringToUint64(" ", &result).has_error());
// We don't allow "+" for positive numbers.
EXPECT_TRUE(StringToUint64("+1234", &result).has_error());
EXPECT_EQ(0u, result);
// Trim.
EXPECT_FALSE(StringToUint64(" 1234 ", &result).has_error());
EXPECT_EQ(1234u, result);
// Leading 0's should still be decimal, don't trigger octal.
EXPECT_FALSE(StringToUint64("01234", &result).has_error());
EXPECT_EQ(1234u, result);
// Hex digits invalid without proper prefix.
EXPECT_TRUE(StringToUint64("12a34", &result).has_error());
// Valid hex number
EXPECT_FALSE(StringToUint64("0x1A2a34", &result).has_error());
EXPECT_EQ(0x1a2a34u, result);
// Isolated hex prefix.
EXPECT_TRUE(StringToUint64("0x", &result).has_error());
// Valid hex number with capital X prefix at the max of a 64-bit int.
EXPECT_FALSE(StringToUint64("0XffffFFFFffffFFFF", &result).has_error());
EXPECT_EQ(0xffffFFFFffffFFFFu, result);
}
TEST(CommandUtils, ReadUint64Arg) {
Command cmd;
uint64_t out;
Err err = ReadUint64Arg(cmd, 0, "code", &out);
EXPECT_TRUE(err.has_error());
EXPECT_EQ("Not enough arguments when reading the code.", err.msg());
std::vector<std::string> args;
args.push_back("12");
args.push_back("0x67");
args.push_back("notanumber");
cmd.set_args(std::move(args));
err = ReadUint64Arg(cmd, 0, "code", &out);
EXPECT_FALSE(err.has_error());
EXPECT_EQ(12u, out);
err = ReadUint64Arg(cmd, 1, "code", &out);
EXPECT_FALSE(err.has_error());
EXPECT_EQ(0x67u, out);
err = ReadUint64Arg(cmd, 2, "code", &out);
EXPECT_TRUE(err.has_error());
EXPECT_EQ("Invalid number \"notanumber\" when reading the code.", err.msg());
}
TEST(CommandUtils, ParseHostPort) {
std::string host;
uint16_t port;
// Host good.
EXPECT_FALSE(ParseHostPort("google.com:1234", &host, &port).has_error());
EXPECT_EQ("google.com", host);
EXPECT_EQ(1234, port);
EXPECT_FALSE(ParseHostPort("google.com", "1234", &host, &port).has_error());
EXPECT_EQ("google.com", host);
EXPECT_EQ(1234, port);
// IPv4 Good.
EXPECT_FALSE(ParseHostPort("192.168.0.1:1234", &host, &port).has_error());
EXPECT_EQ("192.168.0.1", host);
EXPECT_EQ(1234, port);
EXPECT_FALSE(ParseHostPort("192.168.0.1", "1234", &host, &port).has_error());
EXPECT_EQ("192.168.0.1", host);
EXPECT_EQ(1234, port);
// IPv6 Good.
EXPECT_FALSE(ParseHostPort("[1234::5678]:1234", &host, &port).has_error());
EXPECT_EQ("1234::5678", host);
EXPECT_EQ(1234, port);
EXPECT_FALSE(ParseHostPort("[1234::5678]", "1234", &host, &port).has_error());
EXPECT_EQ("1234::5678", host);
EXPECT_EQ(1234, port);
EXPECT_FALSE(ParseHostPort("1234::5678", "1234", &host, &port).has_error());
EXPECT_EQ("1234::5678", host);
EXPECT_EQ(1234, port);
// Missing ports.
EXPECT_TRUE(ParseHostPort("google.com", &host, &port).has_error());
EXPECT_TRUE(ParseHostPort("192.168.0.1", &host, &port).has_error());
EXPECT_TRUE(ParseHostPort("1234::5678", &host, &port).has_error());
EXPECT_TRUE(ParseHostPort("[1234::5678]", &host, &port).has_error());
// Bad port values.
EXPECT_TRUE(ParseHostPort("google.com:0", &host, &port).has_error());
EXPECT_TRUE(ParseHostPort("google.com:99999999", &host, &port).has_error());
}
TEST(CommandUtils, FormatIdentifier) {
// Obviously unparseable identifiers should just come through as strings.
std::string gibberish("!@(#*ASDGasdh<@");
EXPECT_EQ(gibberish, FormatIdentifier(gibberish, true).AsString());
// Regular name.
auto output = FormatIdentifier("ThisIsAName", false);
EXPECT_EQ("kNormal \"ThisIsAName\"", output.GetDebugString());
// Regular name with bolding.
output = FormatIdentifier("ThisIsAName", true);
EXPECT_EQ("kHeading \"ThisIsAName\"", output.GetDebugString());
// Hierarchical name.
output = FormatIdentifier("::Foo<int, char*>::Bar<Baz>", true);
EXPECT_EQ(
"kNormal \"::Foo\", "
"kComment \"<int, char*>\", "
"kNormal \"::\", "
"kHeading \"Bar\", "
"kComment \"<Baz>\"",
output.GetDebugString());
}
TEST(CommandUtils, FormatFunctionName) {
auto function = fxl::MakeRefCounted<Function>(Symbol::kTagSubprogram);
function->set_assigned_name("Function");
// Function with no parameters.
EXPECT_EQ("Function()", FormatFunctionName(function.get(), false).AsString());
EXPECT_EQ("Function()", FormatFunctionName(function.get(), true).AsString());
// Add two parameters.
auto int32_type = MakeInt32Type();
auto param_value = MakeVariableForTest("value", int32_type, 0x100, 0x200,
std::vector<uint8_t>());
auto param_other = MakeVariableForTest("other_param", int32_type, 0x100,
0x200, std::vector<uint8_t>());
function->set_parameters({LazySymbol(param_value), LazySymbol(param_other)});
EXPECT_EQ("Function(…)",
FormatFunctionName(function.get(), false).AsString());
EXPECT_EQ("Function(int32_t, int32_t)",
FormatFunctionName(function.get(), true).AsString());
// Put in a namespace and add some templates. This needs a new function
// because the name will be cached above.
function = fxl::MakeRefCounted<Function>(Symbol::kTagSubprogram);
function->set_assigned_name("Function<int>");
function->set_parameters({LazySymbol(param_value), LazySymbol(param_other)});
auto ns = fxl::MakeRefCounted<Namespace>();
ns->set_assigned_name("ns");
function->set_parent(LazySymbol(ns));
EXPECT_EQ(
"kNormal \"ns::\", "
"kHeading \"Function\", "
"kComment \"<int>(…)\"",
FormatFunctionName(function.get(), false).GetDebugString());
function->set_parent(LazySymbol());
}
TEST(CommandUtils, FormatLocation) {
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
EXPECT_EQ("<invalid address>",
FormatLocation(Location(), true, false).AsString());
// Address-only location.
EXPECT_EQ(
"0x12345",
FormatLocation(Location(Location::State::kAddress, 0x12345), false, false)
.AsString());
// Function-only location.
fxl::RefPtr<Function> function(
fxl::MakeRefCounted<Function>(Symbol::kTagSubprogram));
function->set_assigned_name("Func");
function->set_code_ranges(AddressRanges(AddressRange(0x1200, 0x1300)));
EXPECT_EQ("Func() + 0x34 (no line info)",
FormatLocation(Location(0x1234, FileLine(), 0, symbol_context,
LazySymbol(function)),
false, false)
.AsString());
// Same as above but location is before the function address (probably
// something is corrupt). It should omit the offset.
EXPECT_EQ("Func()",
FormatLocation(Location(0x1100, FileLine(), 0, symbol_context,
LazySymbol(function)),
false, false)
.AsString());
// File/line-only location.
EXPECT_EQ("foo.cc:21",
FormatLocation(Location(0x1234, FileLine("/path/foo.cc", 21), 0,
symbol_context),
false, false)
.AsString());
// Full location.
Location loc(0x1234, FileLine("/path/foo.cc", 21), 0, symbol_context,
LazySymbol(function));
EXPECT_EQ("0x1234, Func() • foo.cc:21",
FormatLocation(loc, true, false).AsString());
EXPECT_EQ("Func() • foo.cc:21", FormatLocation(loc, false, false).AsString());
}
TEST(CommandUtils, DescribeFileLine) {
FileLine fl("/path/to/foo.cc", 21);
EXPECT_EQ("/path/to/foo.cc:21", DescribeFileLine(fl, true));
EXPECT_EQ("foo.cc:21", DescribeFileLine(fl, false));
// Missing line number.
EXPECT_EQ("foo.cc:?", DescribeFileLine(FileLine("/path/foo.cc", 0), false));
// Missing both.
EXPECT_EQ("?:?", DescribeFileLine(FileLine(), false));
}
TEST(SetElementsToAdd, TestCase) {
Err err;
AssignType assign_type;
std::vector<std::string> out;
err = SetElementsToAdd({""}, &assign_type, &out);
EXPECT_TRUE(err.has_error());
err = SetElementsToAdd({"option_name"}, &assign_type, &out);
EXPECT_TRUE(err.has_error());
// Assign.
err = SetElementsToAdd({"option_name", "value"}, &assign_type, &out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(assign_type, AssignType::kAssign);
ASSERT_EQ(out.size(), 1u);
EXPECT_EQ(out[0], "value");
// Equal assign.
err = SetElementsToAdd({"option_name", "="}, &assign_type, &out);
EXPECT_TRUE(err.has_error());
err = SetElementsToAdd({"option_name", "=", "value"}, &assign_type, &out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(assign_type, AssignType::kAssign);
ASSERT_EQ(out.size(), 1u);
EXPECT_EQ(out[0], "value");
// Multiple assign.
err =
SetElementsToAdd({"option_name", "value", "value2"}, &assign_type, &out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(assign_type, AssignType::kAssign);
ASSERT_EQ(out.size(), 2u);
EXPECT_EQ(out[0], "value");
EXPECT_EQ(out[1], "value2");
err = SetElementsToAdd({"option_name", "=", "value", "value2"}, &assign_type,
&out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(assign_type, AssignType::kAssign);
ASSERT_EQ(out.size(), 2u);
EXPECT_EQ(out[0], "value");
EXPECT_EQ(out[1], "value2");
// Append.
err = SetElementsToAdd({"option_name", "+="}, &assign_type, &out);
EXPECT_TRUE(err.has_error());
err = SetElementsToAdd({"option_name", "+=", "value"}, &assign_type, &out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(assign_type, AssignType::kAppend);
ASSERT_EQ(out.size(), 1u);
EXPECT_EQ(out[0], "value");
err = SetElementsToAdd({"option_name", "+=", "value", "value2"}, &assign_type,
&out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(assign_type, AssignType::kAppend);
ASSERT_EQ(out.size(), 2u);
EXPECT_EQ(out[0], "value");
EXPECT_EQ(out[1], "value2");
// Remove.
err = SetElementsToAdd({"option_name", "-="}, &assign_type, &out);
EXPECT_TRUE(err.has_error());
err = SetElementsToAdd({"option_name", "-=", "value"}, &assign_type, &out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(assign_type, AssignType::kRemove);
ASSERT_EQ(out.size(), 1u);
EXPECT_EQ(out[0], "value");
err = SetElementsToAdd({"option_name", "-=", "value", "value2"}, &assign_type,
&out);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(assign_type, AssignType::kRemove);
ASSERT_EQ(out.size(), 2u);
EXPECT_EQ(out[0], "value");
EXPECT_EQ(out[1], "value2");
}
} // namespace zxdb