|  | // 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 "src/developer/debug/zxdb/expr/index_walker.h" | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "src/developer/debug/zxdb/expr/expr_parser.h" | 
|  | #include "src/developer/debug/zxdb/symbols/index.h" | 
|  |  | 
|  | namespace zxdb { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | bool EqualsElements(const IndexWalker::Stage& a, const IndexWalker::Stage& b) { | 
|  | if (a.size() != b.size()) | 
|  | return false; | 
|  |  | 
|  | for (const auto& cur : a) { | 
|  | if (std::find(b.begin(), b.end(), cur) == b.end()) | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST(IndexWalker, ComponentMatchesNameOnly) { | 
|  | ParsedIdentifierComponent foo_comp("Foo"); | 
|  | ParsedIdentifierComponent foo_template_comp("Foo", {"A", "b"}); | 
|  |  | 
|  | // Simple name-only comparisons. | 
|  | EXPECT_TRUE(IndexWalker::ComponentMatchesNameOnly("Foo", foo_comp)); | 
|  | EXPECT_FALSE(IndexWalker::ComponentMatchesNameOnly("FooBar", foo_comp)); | 
|  | EXPECT_FALSE(IndexWalker::ComponentMatchesNameOnly("Fo2", foo_comp)); | 
|  |  | 
|  | // Component has a template, the index string doesn't. | 
|  | EXPECT_TRUE(IndexWalker::ComponentMatchesNameOnly("Foo", foo_template_comp)); | 
|  |  | 
|  | // Component has no template, the index does (this input is non-canonical). | 
|  | EXPECT_TRUE(IndexWalker::ComponentMatchesNameOnly("Foo < C >", foo_template_comp)); | 
|  | } | 
|  |  | 
|  | TEST(IndexWalker, ComponentMatchesTemplateOnly) { | 
|  | ParsedIdentifierComponent foo_comp("Foo"); | 
|  | ParsedIdentifierComponent foo_template_comp("Foo", {"A", "b"}); | 
|  | ParsedIdentifierComponent foo_empty_template_comp("Foo", {}); | 
|  |  | 
|  | // Neither inputs have templates (should be a match). | 
|  | EXPECT_TRUE(IndexWalker::ComponentMatchesTemplateOnly("Foo", foo_comp)); | 
|  |  | 
|  | // Template match but with different whitespace. | 
|  | EXPECT_TRUE(IndexWalker::ComponentMatchesTemplateOnly("Foo < A,  b > ", foo_template_comp)); | 
|  |  | 
|  | // One has a template but the other doesn't. | 
|  | EXPECT_FALSE(IndexWalker::ComponentMatchesTemplateOnly("Foo", foo_template_comp)); | 
|  | EXPECT_FALSE(IndexWalker::ComponentMatchesTemplateOnly("Foo<C>", foo_comp)); | 
|  |  | 
|  | // Empty template doesn't match no template. | 
|  | EXPECT_FALSE(IndexWalker::ComponentMatchesTemplateOnly("Foo<>", foo_comp)); | 
|  | EXPECT_FALSE(IndexWalker::ComponentMatchesTemplateOnly("Foo", foo_empty_template_comp)); | 
|  | } | 
|  |  | 
|  | // Most cases are tested by ComponentMatchesNameOnly and ...TemplateOnly above. | 
|  | TEST(IndexWalker, ComponentMatches) { | 
|  | ParsedIdentifierComponent foo_comp("Foo"); | 
|  | ParsedIdentifierComponent foo_template_comp("Foo", {"A", "b"}); | 
|  |  | 
|  | EXPECT_TRUE(IndexWalker::ComponentMatches("Foo", foo_comp)); | 
|  | EXPECT_FALSE(IndexWalker::ComponentMatches("Foo<>", foo_comp)); | 
|  | EXPECT_FALSE(IndexWalker::ComponentMatches("Foo<>", foo_template_comp)); | 
|  | EXPECT_TRUE(IndexWalker::ComponentMatches("Foo <A,b >", foo_template_comp)); | 
|  | } | 
|  |  | 
|  | TEST(IndexWalker, IsIndexStringBeyondName) { | 
|  | // Identity comparison. | 
|  | EXPECT_FALSE(IndexWalker::IsIndexStringBeyondName("Foo", "Foo")); | 
|  |  | 
|  | // Index nodes clearly before. | 
|  | EXPECT_FALSE(IndexWalker::IsIndexStringBeyondName("Fo", "Foo")); | 
|  | EXPECT_FALSE(IndexWalker::IsIndexStringBeyondName("Foa", "Foo")); | 
|  |  | 
|  | // Index nodes clearly after. | 
|  | EXPECT_TRUE(IndexWalker::IsIndexStringBeyondName("FooBar", "Foo")); | 
|  | EXPECT_TRUE(IndexWalker::IsIndexStringBeyondName("Foz", "Foo")); | 
|  | EXPECT_TRUE(IndexWalker::IsIndexStringBeyondName("Fz", "Foo")); | 
|  |  | 
|  | // Templates in the index could has "not beyond". | 
|  | EXPECT_FALSE(IndexWalker::IsIndexStringBeyondName("Foo<a>", "Foo")); | 
|  | } | 
|  |  | 
|  | TEST(IndexWalker, WalkInto) { | 
|  | Index index; | 
|  | auto& root = index.root(); | 
|  | auto foo_node = root.AddChild(IndexNode::Kind::kType, "Foo"); | 
|  | root.AddChild(IndexNode::Kind::kType, "Foo<Bar>"); | 
|  |  | 
|  | // These template names are non-canonical so we can verify the correct comparisons happen. | 
|  | foo_node->AddChild(IndexNode::Kind::kType, "Bar< int >"); | 
|  | auto bar_int_char_node = foo_node->AddChild(IndexNode::Kind::kType, "Bar< int,char >"); | 
|  |  | 
|  | // There could also be a non-template somewhere with the same name. | 
|  | auto bar_node = foo_node->AddChild(IndexNode::Kind::kType, "Bar"); | 
|  |  | 
|  | // These nodes start with the prefix "Bar" for when we're searching. We test things that will | 
|  | // compare before and after "Bar<" ('9' before, 'f' after). | 
|  | auto barf_node = foo_node->AddChild(IndexNode::Kind::kType, "Barf<int>"); | 
|  | auto bar9_node = foo_node->AddChild(IndexNode::Kind::kType, "Bar9<int>"); | 
|  |  | 
|  | IndexWalker walker(&index); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), {&root})); | 
|  |  | 
|  | // Walking up at this point should be a no-op. | 
|  | EXPECT_FALSE(walker.WalkUp()); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), {&root})); | 
|  |  | 
|  | // Walk to the "Foo" component. | 
|  | EXPECT_TRUE(walker.WalkInto(ParsedIdentifierComponent("Foo"))); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), {foo_node})); | 
|  |  | 
|  | // Walk to the "NotPresent" component. The current location should be unchanged. | 
|  | EXPECT_FALSE(walker.WalkInto(ParsedIdentifierComponent("NotFound"))); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), {foo_node})); | 
|  |  | 
|  | // Walk to the "Bar<int,char>" identifier. | 
|  | ParsedIdentifier bar_int_char; | 
|  | Err err = ExprParser::ParseIdentifier("Bar < int , char >", &bar_int_char); | 
|  | EXPECT_FALSE(err.has_error()) << err.msg(); | 
|  | EXPECT_TRUE(walker.WalkInto(bar_int_char)); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), {bar_int_char_node})); | 
|  |  | 
|  | // Walk back up to "Foo". | 
|  | EXPECT_TRUE(walker.WalkUp()); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), {foo_node})); | 
|  |  | 
|  | // Walk to the "Bar" node. | 
|  | EXPECT_TRUE(walker.WalkInto(ParsedIdentifierComponent("Bar"))); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), {bar_node})); | 
|  |  | 
|  | // Parse the Barf identifier for the following two tests. This one has a toplevel scope. | 
|  | ParsedIdentifier barf; | 
|  | err = ExprParser::ParseIdentifier("::Foo::Barf<int>", &barf); | 
|  | EXPECT_FALSE(err.has_error()) << err.msg(); | 
|  |  | 
|  | // Walk to the "Foo::Bar9<int>" with copying the walker. | 
|  | { | 
|  | IndexWalker nested_walker(walker); | 
|  | ParsedIdentifier bar9; | 
|  | err = ExprParser::ParseIdentifier(":: Foo :: Bar9 < int >", &bar9); | 
|  | EXPECT_FALSE(err.has_error()) << err.msg(); | 
|  | EXPECT_TRUE(nested_walker.WalkInto(bar9)); | 
|  | EXPECT_TRUE(EqualsElements(nested_walker.current(), {bar9_node})); | 
|  | } | 
|  |  | 
|  | // Walking from the root into the barf template should work. | 
|  | EXPECT_TRUE(walker.WalkInto(barf)); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), {barf_node})); | 
|  | } | 
|  |  | 
|  | // Tests that we can walk into multiple nodes of different types (namespaces, functions, etc.) at | 
|  | // the same time when they have the same name. | 
|  | TEST(IndexWalker, WalkIntoMultiple) { | 
|  | Index index; | 
|  | auto& root = index.root(); | 
|  | const char kFoo[] = "Foo"; | 
|  | auto foo_type_node = root.AddChild(IndexNode::Kind::kType, kFoo); | 
|  | auto foo_ns_node = root.AddChild(IndexNode::Kind::kNamespace, kFoo); | 
|  | auto foo_func_node = root.AddChild(IndexNode::Kind::kFunction, kFoo); | 
|  | auto foo_var_node = root.AddChild(IndexNode::Kind::kVar, kFoo); | 
|  |  | 
|  | const char kBar[] = "Bar"; | 
|  | auto foo_bar_type_func = foo_type_node->AddChild(IndexNode::Kind::kFunction, kBar); | 
|  | auto foo_bar_ns_func = foo_ns_node->AddChild(IndexNode::Kind::kFunction, kBar); | 
|  |  | 
|  | IndexWalker walker(&index); | 
|  | IndexWalker::Stage expected_root{&root}; | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_root)); | 
|  |  | 
|  | // Walking into "Foo" should identify all 4 categories of thing. | 
|  | EXPECT_TRUE(walker.WalkInto(ParsedIdentifierComponent(kFoo))); | 
|  | IndexWalker::Stage expected_foo{foo_type_node, foo_ns_node, foo_func_node, foo_var_node}; | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_foo)); | 
|  |  | 
|  | // Walking into "Bar" from there should narrow down to two. | 
|  | EXPECT_TRUE(walker.WalkInto(ParsedIdentifierComponent(kBar))); | 
|  | IndexWalker::Stage expected_foo_bar{foo_bar_type_func, foo_bar_ns_func}; | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_foo_bar)); | 
|  |  | 
|  | // Walking into something that doesn't exist reports failure and stays in the same place. | 
|  | EXPECT_FALSE(walker.WalkInto(ParsedIdentifierComponent("Nonexistant"))); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_foo_bar)); | 
|  |  | 
|  | // Walk up should give the same results. | 
|  | EXPECT_TRUE(walker.WalkUp()); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_foo)); | 
|  | EXPECT_TRUE(walker.WalkUp()); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_root)); | 
|  |  | 
|  | // Going up above the root fails and does nothing. | 
|  | EXPECT_FALSE(walker.WalkUp()); | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_root)); | 
|  | } | 
|  |  | 
|  | TEST(IndexWalker, WalkIntoAnonNS) { | 
|  | Index index; | 
|  | auto& root = index.root(); | 
|  |  | 
|  | // Anonymous and "ns" namespace at the top level. | 
|  | auto anon_ns = root.AddChild(IndexNode::Kind::kNamespace, ""); | 
|  | const char kNsName[] = "ns"; | 
|  | auto ns_ns = root.AddChild(IndexNode::Kind::kNamespace, kNsName); | 
|  |  | 
|  | // Add a "Struct" at the toplevel and in the anon namespace. | 
|  | const char kStructName[] = "Struct"; | 
|  | auto toplevel_struct = root.AddChild(IndexNode::Kind::kType, kStructName); | 
|  | auto anon_struct = anon_ns->AddChild(IndexNode::Kind::kType, kStructName); | 
|  |  | 
|  | // Add a ns::<anon>::<anon>::OtherStruct type. | 
|  | const char kOtherStructName[] = "OtherStruct"; | 
|  | auto ns_anon_ns = ns_ns->AddChild(IndexNode::Kind::kNamespace, ""); | 
|  | auto ns_anon_anon_ns = ns_anon_ns->AddChild(IndexNode::Kind::kNamespace, ""); | 
|  | auto otherstruct = ns_anon_anon_ns->AddChild(IndexNode::Kind::kType, kOtherStructName); | 
|  |  | 
|  | IndexWalker walker(&index); | 
|  | // The initial search should be in the root and in the toplevel anonymous namespace. | 
|  | IndexWalker::Stage expected_root{&root, anon_ns}; | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_root)); | 
|  |  | 
|  | // Walking into "Struct" should give both since we implicitly went into the anon namespace. | 
|  | EXPECT_TRUE(walker.WalkInto(ParsedIdentifier(kStructName))); | 
|  | IndexWalker::Stage expected_struct{toplevel_struct, anon_struct}; | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_struct)); | 
|  |  | 
|  | // Go back to the root. | 
|  | EXPECT_TRUE(walker.WalkUp()); | 
|  |  | 
|  | // Walk into "ns::OtherStruct" which should find the one with two anon namespaces in it. | 
|  | ParsedIdentifier ns_otherstruct; | 
|  | ASSERT_TRUE(ExprParser::ParseIdentifier("ns::OtherStruct", &ns_otherstruct).ok()); | 
|  | EXPECT_TRUE(walker.WalkInto(ns_otherstruct)); | 
|  | IndexWalker::Stage expected_otherstruct{otherstruct}; | 
|  | EXPECT_TRUE(EqualsElements(walker.current(), expected_otherstruct)); | 
|  | } | 
|  |  | 
|  | }  // namespace zxdb |