blob: 0f1cfff42f9296c7ca21696fa63e707cd80b0d78 [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 "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