blob: e54f0f1391e2d6b03be1572e5152e6c06e015200 [file] [log] [blame]
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "common/util/map_tree.h"
#include <ostream>
#include <sstream>
#include <string>
#include <utility>
#include "absl/strings/string_view.h"
#include "common/util/spacer.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace verible {
namespace {
using MapTreeTestType = MapTree<int, std::string>;
using KV = MapTreeTestType::key_value_type;
TEST(MapTreeTest, EmptyConstruction) {
const MapTreeTestType m;
EXPECT_TRUE(m.is_leaf());
EXPECT_TRUE(m.Children().empty());
EXPECT_EQ(m.Parent(), nullptr);
EXPECT_EQ(m.begin(), m.end());
EXPECT_EQ(m.NumAncestors(), 0);
EXPECT_FALSE(m.HasAncestor(nullptr));
EXPECT_FALSE(m.HasAncestor(&m));
EXPECT_EQ(m.Root(), &m);
EXPECT_EQ(m.KeyValuePair(), nullptr);
EXPECT_EQ(m.Key(), nullptr);
EXPECT_EQ(m.Find(0), m.end()); // there are no keys
EXPECT_TRUE(m.Value().empty()); // default constructed std::string
}
TEST(MapTreeTest, InitializedValueConstruction) {
const MapTreeTestType m("boofar"); // given initial value
EXPECT_TRUE(m.is_leaf());
EXPECT_TRUE(m.Children().empty());
EXPECT_EQ(m.Parent(), nullptr);
EXPECT_EQ(m.begin(), m.end());
EXPECT_EQ(m.NumAncestors(), 0);
EXPECT_FALSE(m.HasAncestor(nullptr));
EXPECT_FALSE(m.HasAncestor(&m));
EXPECT_EQ(m.Root(), &m);
EXPECT_EQ(m.KeyValuePair(), nullptr);
EXPECT_EQ(m.Key(), nullptr);
EXPECT_EQ(m.Find(0), m.end()); // there are no keys
EXPECT_EQ(m.Value(), "boofar");
}
TEST(MapTreeTest, InitializeOneChild) {
const MapTreeTestType m("foo", //
KV{3, MapTreeTestType("bar")});
EXPECT_FALSE(m.is_leaf());
EXPECT_EQ(m.Children().size(), 1);
EXPECT_EQ(m.Parent(), nullptr);
EXPECT_NE(m.begin(), m.end());
EXPECT_EQ(m.NumAncestors(), 0);
EXPECT_EQ(m.Root(), &m);
EXPECT_EQ(m.KeyValuePair(), nullptr);
EXPECT_EQ(m.Key(), nullptr);
EXPECT_EQ(m.Find(0), m.end());
EXPECT_EQ(m.Value(), "foo");
const auto found = m.Find(3);
ASSERT_NE(found, m.end());
EXPECT_EQ(found->first, 3);
EXPECT_EQ(found->second.Value(), "bar");
EXPECT_EQ(found->second.KeyValuePair(), &*found);
EXPECT_EQ(found->second.Key(), &found->first);
EXPECT_EQ(*found->second.Key(), 3);
EXPECT_EQ(found->second.Parent(), &m);
EXPECT_EQ(found->second.Root(), &m);
EXPECT_TRUE(found->second.is_leaf());
EXPECT_EQ(found->second.NumAncestors(), 1);
EXPECT_FALSE(m.HasAncestor(&found->second));
EXPECT_TRUE(found->second.HasAncestor(&m));
}
TEST(MapTreeTest, EmplaceOneChild) {
// Same structure as InitializeOneChild, but emplacing after construction.
MapTreeTestType m("foo");
const auto p = m.TryEmplace(3, "bar");
ASSERT_TRUE(p.second);
EXPECT_EQ(p.first, m.begin());
EXPECT_EQ(p.first->second.Value(), "bar");
EXPECT_FALSE(m.is_leaf());
EXPECT_EQ(m.Children().size(), 1);
EXPECT_EQ(m.Parent(), nullptr);
EXPECT_NE(m.begin(), m.end());
EXPECT_EQ(m.NumAncestors(), 0);
EXPECT_EQ(m.Root(), &m);
EXPECT_EQ(m.KeyValuePair(), nullptr);
EXPECT_EQ(m.Key(), nullptr);
EXPECT_EQ(m.Find(0), m.end());
EXPECT_EQ(m.Value(), "foo");
const auto found = m.Find(3);
ASSERT_NE(found, m.end());
EXPECT_EQ(found->first, 3);
EXPECT_EQ(found->second.Value(), "bar");
EXPECT_EQ(found->second.KeyValuePair(), &*found);
EXPECT_EQ(found->second.Key(), &found->first);
EXPECT_EQ(*found->second.Key(), 3);
EXPECT_EQ(found->second.Parent(), &m);
EXPECT_EQ(found->second.Root(), &m);
EXPECT_TRUE(found->second.is_leaf());
EXPECT_EQ(found->second.NumAncestors(), 1);
EXPECT_FALSE(m.HasAncestor(&found->second));
EXPECT_TRUE(found->second.HasAncestor(&m));
}
struct NonCopyable {
absl::string_view text;
explicit NonCopyable(absl::string_view text) : text(text) {}
// move-only, no copy
NonCopyable(const NonCopyable &) = delete;
NonCopyable(NonCopyable &&) = default;
NonCopyable &operator=(const NonCopyable &) = delete;
NonCopyable &operator=(NonCopyable &&) = default;
};
TEST(MapTreeTest, EmplaceOneNonCopyable) {
// Same structure as EmplaceOneChild, but on a non-copyable type.
using NoCopyMapTreeTestType = MapTree<int, NonCopyable>;
NoCopyMapTreeTestType m(NonCopyable("foo"));
const auto p = m.TryEmplace(3, NonCopyable("bar"));
ASSERT_TRUE(p.second);
EXPECT_EQ(p.first, m.begin());
EXPECT_EQ(p.first->second.Value().text, "bar");
EXPECT_FALSE(m.is_leaf());
EXPECT_EQ(m.Children().size(), 1);
EXPECT_EQ(m.Parent(), nullptr);
EXPECT_NE(m.begin(), m.end());
EXPECT_EQ(m.NumAncestors(), 0);
EXPECT_EQ(m.Root(), &m);
EXPECT_EQ(m.KeyValuePair(), nullptr);
EXPECT_EQ(m.Key(), nullptr);
EXPECT_EQ(m.Find(0), m.end());
EXPECT_EQ(m.Value().text, "foo");
const auto found = m.Find(3);
ASSERT_NE(found, m.end());
EXPECT_EQ(found->first, 3);
EXPECT_EQ(found->second.Value().text, "bar");
EXPECT_EQ(found->second.KeyValuePair(), &*found);
EXPECT_EQ(found->second.Key(), &found->first);
EXPECT_EQ(*found->second.Key(), 3);
EXPECT_EQ(found->second.Parent(), &m);
EXPECT_EQ(found->second.Root(), &m);
EXPECT_TRUE(found->second.is_leaf());
EXPECT_EQ(found->second.NumAncestors(), 1);
EXPECT_FALSE(m.HasAncestor(&found->second));
EXPECT_TRUE(found->second.HasAncestor(&m));
}
TEST(MapTreeTest, EmplaceDuplicateKeyFails) {
MapTreeTestType m("foo", KV{2, MapTreeTestType("bar")});
const auto p = m.TryEmplace(2, "zzr");
EXPECT_FALSE(p.second);
EXPECT_EQ(p.first, m.begin());
EXPECT_EQ(p.first->second.Value(), "bar"); // first entry retained
EXPECT_EQ(m.Children().size(), 1);
}
TEST(MapTreeTest, EmplaceSecondKey) {
MapTreeTestType m("foo", KV{9, MapTreeTestType("bar")});
const auto first_iter = m.begin();
EXPECT_EQ(first_iter->first, 9);
const auto p = m.TryEmplace(7, "zzr");
EXPECT_TRUE(p.second); // successful insertion
EXPECT_EQ(m.Children().size(), 2);
EXPECT_EQ(p.first->first, 7);
EXPECT_EQ(p.first->second.Value(), "zzr");
EXPECT_EQ(m.Find(9), first_iter); // iterator stability on insert
}
TEST(MapTreeTest, InitializeMultipleChildrenWithDuplicateKey) {
const MapTreeTestType m("foo", //
KV{4, MapTreeTestType("bbb")},
KV{4, MapTreeTestType("cccc")} // will be dropped
);
EXPECT_FALSE(m.is_leaf());
EXPECT_EQ(m.Children().size(), 1); // not 2
EXPECT_EQ(m.Parent(), nullptr);
EXPECT_NE(m.begin(), m.end());
EXPECT_EQ(m.NumAncestors(), 0);
EXPECT_EQ(m.Root(), &m);
EXPECT_EQ(m.KeyValuePair(), nullptr);
EXPECT_EQ(m.Key(), nullptr);
EXPECT_EQ(m.Find(0), m.end());
EXPECT_EQ(m.Value(), "foo");
const auto found = m.Find(4);
ASSERT_NE(found, m.end());
EXPECT_EQ(found->second.Value(), "bbb");
}
TEST(MapTreeTest, InitializeMultipleChildrenWithDistinctKeys) {
const MapTreeTestType m("foo", //
KV{4, MapTreeTestType("bbb")},
KV{1, MapTreeTestType("dd")});
EXPECT_FALSE(m.is_leaf());
EXPECT_EQ(m.Children().size(), 2);
EXPECT_EQ(m.Parent(), nullptr);
EXPECT_NE(m.begin(), m.end());
EXPECT_EQ(m.NumAncestors(), 0);
EXPECT_EQ(m.Root(), &m);
EXPECT_EQ(m.KeyValuePair(), nullptr);
EXPECT_EQ(m.Key(), nullptr);
EXPECT_EQ(m.Find(0), m.end());
EXPECT_EQ(m.Value(), "foo");
{
const auto found = m.Find(4);
ASSERT_NE(found, m.end());
EXPECT_EQ(found->second.Value(), "bbb");
}
{
const auto found = m.Find(1);
ASSERT_NE(found, m.end());
EXPECT_EQ(found->second.Value(), "dd");
}
EXPECT_EQ(m.Find(2), m.end());
for (const auto &p : m) {
EXPECT_EQ(p.second.KeyValuePair(), &p);
EXPECT_EQ(p.second.Key(), &p.first);
EXPECT_EQ(p.second.Parent(), &m);
EXPECT_EQ(p.second.Root(), &m);
EXPECT_EQ(p.second.NumAncestors(), 1);
EXPECT_TRUE(p.second.HasAncestor(&m));
EXPECT_TRUE(p.second.is_leaf());
}
{
const auto found1 = m.Find(1);
const auto found2 = m.Find(4);
EXPECT_FALSE(found1->second.HasAncestor(&found2->second));
EXPECT_FALSE(found2->second.HasAncestor(&found1->second));
}
}
TEST(MapTreeTest, InitializeTwoGenerationsDeepCopy) {
const MapTreeTestType m("foo", //
KV{4, // child
MapTreeTestType("bbb",
KV{1, // grandchild
MapTreeTestType("dd")})});
const auto check = [](const MapTreeTestType &root) {
const auto child = root.Find(4);
const auto grandchild = child->second.Find(1);
EXPECT_EQ(child->first, 4);
EXPECT_EQ(child->second.Value(), "bbb");
EXPECT_EQ(child->second.Parent(), &root);
EXPECT_FALSE(child->second.is_leaf());
EXPECT_EQ(child->second.Children().size(), 1);
EXPECT_EQ(child->second.NumAncestors(), 1);
EXPECT_TRUE(child->second.HasAncestor(&root));
EXPECT_EQ(grandchild->first, 1);
EXPECT_EQ(grandchild->second.Value(), "dd");
EXPECT_EQ(grandchild->second.Parent(), &child->second);
EXPECT_TRUE(grandchild->second.is_leaf());
EXPECT_EQ(grandchild->second.NumAncestors(), 2);
EXPECT_EQ(grandchild->second.Root(), &root);
EXPECT_TRUE(grandchild->second.HasAncestor(&child->second));
EXPECT_TRUE(grandchild->second.HasAncestor(&root));
};
check(m);
{
// specificially testing deep copy.
// clang-format off
const MapTreeTestType mcopy(m); // NOLINT(performance-unnecessary-copy-initialization)
// clang-format on
check(mcopy);
check(m);
}
}
TEST(MapTreeTest, InitializeTwoGenerationsMove) {
MapTreeTestType m("foo", //
KV{4, // child
MapTreeTestType("bbb",
KV{1, // grandchild
MapTreeTestType("dd")})});
MapTreeTestType m_moved(std::move(m));
{
const auto child = m_moved.Find(4);
const auto grandchild = child->second.Find(1);
EXPECT_EQ(child->first, 4);
EXPECT_EQ(child->second.Value(), "bbb");
EXPECT_EQ(child->second.Parent(), &m_moved);
EXPECT_FALSE(child->second.is_leaf());
EXPECT_EQ(child->second.Children().size(), 1);
EXPECT_EQ(child->second.NumAncestors(), 1);
EXPECT_TRUE(child->second.HasAncestor(&m_moved));
EXPECT_EQ(grandchild->first, 1);
EXPECT_EQ(grandchild->second.Value(), "dd");
EXPECT_EQ(grandchild->second.Parent(), &child->second);
EXPECT_TRUE(grandchild->second.is_leaf());
EXPECT_EQ(grandchild->second.NumAncestors(), 2);
EXPECT_EQ(grandchild->second.Root(), &m_moved);
EXPECT_TRUE(grandchild->second.HasAncestor(&child->second));
EXPECT_TRUE(grandchild->second.HasAncestor(&m_moved));
}
}
TEST(MapTreeTest, Swap) {
MapTreeTestType m1("foo", //
KV{4, // child
MapTreeTestType("bbb",
KV{1, // grandchild
MapTreeTestType("dd")})});
MapTreeTestType m2("foo", //
KV{2, // child
MapTreeTestType("aaaa")});
const auto check1 = [](const MapTreeTestType &root) {
const auto child = root.Find(4);
const auto grandchild = child->second.Find(1);
EXPECT_EQ(child->first, 4);
EXPECT_EQ(child->second.Value(), "bbb");
EXPECT_EQ(child->second.Parent(), &root);
EXPECT_FALSE(child->second.is_leaf());
EXPECT_EQ(child->second.Children().size(), 1);
EXPECT_EQ(child->second.NumAncestors(), 1);
EXPECT_EQ(grandchild->first, 1);
EXPECT_EQ(grandchild->second.Value(), "dd");
EXPECT_EQ(grandchild->second.Parent(), &child->second);
EXPECT_TRUE(grandchild->second.is_leaf());
EXPECT_EQ(grandchild->second.NumAncestors(), 2);
EXPECT_EQ(grandchild->second.Root(), &root);
};
const auto check2 = [](const MapTreeTestType &root) {
const auto child = root.Find(2);
EXPECT_EQ(child->first, 2);
EXPECT_EQ(child->second.Value(), "aaaa");
EXPECT_EQ(child->second.Parent(), &root);
EXPECT_TRUE(child->second.is_leaf());
EXPECT_TRUE(child->second.Children().empty());
};
check1(m1);
check2(m2);
m1.swap(m2);
check1(m2);
check2(m1);
}
TEST(MapTreeTest, TraversePrint) {
const MapTreeTestType m(
"groot", //
KV{5, MapTreeTestType("pp", KV{4, MapTreeTestType("ss")},
KV{1, MapTreeTestType("tt")})},
KV{3, MapTreeTestType("qq", KV{2, MapTreeTestType("ww")},
KV{6, MapTreeTestType("vv")})});
// printing has the benefit of verifying traversal order
// pre-order traversals
{ // print node values (string_views)
std::ostringstream stream;
m.ApplyPreOrder([&stream](const MapTreeTestType::node_value_type &v) {
stream << v << " ";
});
EXPECT_EQ(stream.str(), "groot qq ww vv pp tt ss ");
}
{ // print keys (ints)
std::ostringstream stream;
m.ApplyPreOrder([&stream](const MapTreeTestType &node) {
const auto *key = node.Key();
stream << (key == nullptr ? 0 : *key) << " ";
});
EXPECT_EQ(stream.str(), "0 3 2 6 5 1 4 ");
}
// post-order traversals
{ // print node values (string_views)
std::ostringstream stream;
m.ApplyPostOrder([&stream](const MapTreeTestType::node_value_type &v) {
stream << v << " ";
});
EXPECT_EQ(stream.str(), "ww vv qq tt ss pp groot ");
}
{ // print keys (ints)
std::ostringstream stream;
m.ApplyPostOrder([&stream](const MapTreeTestType &node) {
const auto *key = node.Key();
stream << (key == nullptr ? 0 : *key) << " ";
});
EXPECT_EQ(stream.str(), "2 6 3 1 4 5 0 ");
}
}
TEST(MapTreeTest, TraverseMutate) {
const MapTreeTestType m(
"groot", //
KV{5, MapTreeTestType("pp", KV{4, MapTreeTestType("ss")},
KV{1, MapTreeTestType("tt")})},
KV{3, MapTreeTestType("qq", KV{2, MapTreeTestType("ww")},
KV{6, MapTreeTestType("vv")})});
// pre-order traversals
{ // mutate node values (string_views)
MapTreeTestType m_copy(m); // deep copy, mutate this copy
std::ostringstream stream;
m_copy.ApplyPreOrder([&stream](MapTreeTestType::node_value_type &v) {
v = v.substr(1); // mutate: truncate
stream << v << " "; // print to verify order
});
EXPECT_EQ(stream.str(), "root q w v p t s ");
}
{ // mutate nodes
MapTreeTestType m_copy(m); // deep copy, mutate this copy
std::ostringstream stream;
m_copy.ApplyPreOrder([&stream](MapTreeTestType &v) {
v.Value() = v.Value().substr(1); // mutate: truncate
stream << v.Value() << " "; // print to verify order
});
EXPECT_EQ(stream.str(), "root q w v p t s ");
}
// post-order traversals
{ // mutate node values (string_views)
MapTreeTestType m_copy(m); // deep copy, mutate this copy
std::ostringstream stream;
m_copy.ApplyPostOrder([&stream](MapTreeTestType::node_value_type &v) {
v = v.substr(1); // mutate: truncate
stream << v << " "; // print to verify order
});
EXPECT_EQ(stream.str(), "w v q t s p root ");
}
{ // mutate nodes
MapTreeTestType m_copy(m); // deep copy, mutate this copy
std::ostringstream stream;
m_copy.ApplyPostOrder([&stream](MapTreeTestType &v) {
v.Value() = v.Value().substr(1); // mutate: truncate
stream << v.Value() << " "; // print to verify order
});
EXPECT_EQ(stream.str(), "w v q t s p root ");
}
}
TEST(MapTreeTest, PrintTreeRootOnly) {
const MapTreeTestType m("groot");
std::ostringstream stream;
m.PrintTree(stream);
EXPECT_EQ(stream.str(), "{ (groot) }");
}
TEST(MapTreeTest, PrintTreeOneChild) {
const MapTreeTestType m("groot", KV{5, MapTreeTestType("gleaf")});
std::ostringstream stream;
m.PrintTree(stream);
EXPECT_EQ(stream.str(), //
"{ (groot)\n"
" 5: { (gleaf) }\n"
"}");
}
TEST(MapTreeTest, PrintTreeTwoGenerations) {
const MapTreeTestType m(
"groot", //
KV{5, MapTreeTestType("pp", KV{4, MapTreeTestType("ss")},
KV{1, MapTreeTestType("tt")})},
KV{3, MapTreeTestType("qq", KV{2, MapTreeTestType("ww")},
KV{6, MapTreeTestType("vv")})});
std::ostringstream stream;
m.PrintTree(stream);
EXPECT_EQ(stream.str(), //
"{ (groot)\n"
" 3: { (qq)\n"
" 2: { (ww) }\n"
" 6: { (vv) }\n"
" }\n"
" 5: { (pp)\n"
" 1: { (tt) }\n"
" 4: { (ss) }\n"
" }\n"
"}");
}
TEST(MapTreeTest, PrintTreeTwoGenerationsUsingIndent) {
const MapTreeTestType m(
"groot", //
KV{5, MapTreeTestType("pp", KV{4, MapTreeTestType("ss")},
KV{1, MapTreeTestType("tt")})},
KV{3, MapTreeTestType("qq", KV{2, MapTreeTestType("ww")},
KV{6, MapTreeTestType("vv")})});
std::ostringstream stream;
m.PrintTree(stream,
[](std::ostream &s, const std::string &text,
size_t indent) -> std::ostream & {
const Spacer wrap(indent + 4);
for (const auto c : text) {
s << '\n' << wrap << c;
}
return s << '\n' << Spacer(indent);
});
EXPECT_EQ(stream.str(), //
R"({ (
g
r
o
o
t
)
3: { (
q
q
)
2: { (
w
w
) }
6: { (
v
v
) }
}
5: { (
p
p
)
1: { (
t
t
) }
4: { (
s
s
) }
}
})");
}
} // namespace
} // namespace verible