| // 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 |