// Copyright 2018 The Abseil 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 // // https://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 "absl/container/node_hash_map.h" #include "absl/container/internal/tracked.h" #include "absl/container/internal/unordered_map_constructor_test.h" #include "absl/container/internal/unordered_map_lookup_test.h" #include "absl/container/internal/unordered_map_members_test.h" #include "absl/container/internal/unordered_map_modifiers_test.h" namespace absl { namespace container_internal { namespace { using ::testing::Field; using ::testing::Pair; using ::testing::UnorderedElementsAre; using MapTypes = ::testing::Types< absl::node_hash_map>>, absl::node_hash_map>>>; INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ConstructorTest, MapTypes); INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, LookupTest, MapTypes); INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, MembersTest, MapTypes); INSTANTIATE_TYPED_TEST_SUITE_P(NodeHashMap, ModifiersTest, MapTypes); using M = absl::node_hash_map>; TEST(NodeHashMap, Emplace) { M m; Tracked t(53); m.emplace("a", t); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(1, t.num_copies()); m.emplace(std::string("a"), t); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(1, t.num_copies()); std::string a("a"); m.emplace(a, t); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(1, t.num_copies()); const std::string ca("a"); m.emplace(a, t); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(1, t.num_copies()); m.emplace(std::make_pair("a", t)); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(2, t.num_copies()); m.emplace(std::make_pair(std::string("a"), t)); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(3, t.num_copies()); std::pair> p("a", t); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(4, t.num_copies()); m.emplace(p); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(4, t.num_copies()); const std::pair> cp("a", t); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(5, t.num_copies()); m.emplace(cp); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(5, t.num_copies()); std::pair> pc("a", t); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(6, t.num_copies()); m.emplace(pc); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(6, t.num_copies()); const std::pair> cpc("a", t); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(7, t.num_copies()); m.emplace(cpc); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(7, t.num_copies()); m.emplace(std::piecewise_construct, std::forward_as_tuple("a"), std::forward_as_tuple(t)); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(7, t.num_copies()); m.emplace(std::piecewise_construct, std::forward_as_tuple(std::string("a")), std::forward_as_tuple(t)); ASSERT_EQ(0, t.num_moves()); ASSERT_EQ(7, t.num_copies()); } TEST(NodeHashMap, AssignRecursive) { struct Tree { // Verify that unordered_map can be instantiated. absl::node_hash_map children; }; Tree root; const Tree& child = root.children.emplace().first->second; // Verify that `lhs = rhs` doesn't read rhs after clearing lhs. root = child; } TEST(FlatHashMap, MoveOnlyKey) { struct Key { Key() = default; Key(Key&&) = default; Key& operator=(Key&&) = default; }; struct Eq { bool operator()(const Key&, const Key&) const { return true; } }; struct Hash { size_t operator()(const Key&) const { return 0; } }; absl::node_hash_map m; m[Key()]; } struct NonMovableKey { explicit NonMovableKey(int i) : i(i) {} NonMovableKey(NonMovableKey&&) = delete; int i; }; struct NonMovableKeyHash { using is_transparent = void; size_t operator()(const NonMovableKey& k) const { return k.i; } size_t operator()(int k) const { return k; } }; struct NonMovableKeyEq { using is_transparent = void; bool operator()(const NonMovableKey& a, const NonMovableKey& b) const { return a.i == b.i; } bool operator()(const NonMovableKey& a, int b) const { return a.i == b; } }; TEST(NodeHashMap, MergeExtractInsert) { absl::node_hash_map set1, set2; set1.emplace(std::piecewise_construct, std::make_tuple(7), std::make_tuple(-7)); set1.emplace(std::piecewise_construct, std::make_tuple(17), std::make_tuple(-17)); set2.emplace(std::piecewise_construct, std::make_tuple(7), std::make_tuple(-70)); set2.emplace(std::piecewise_construct, std::make_tuple(19), std::make_tuple(-190)); auto Elem = [](int key, int value) { return Pair(Field(&NonMovableKey::i, key), value); }; EXPECT_THAT(set1, UnorderedElementsAre(Elem(7, -7), Elem(17, -17))); EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70), Elem(19, -190))); // NonMovableKey is neither copyable nor movable. We should still be able to // move nodes around. static_assert(!std::is_move_constructible::value, ""); set1.merge(set2); EXPECT_THAT(set1, UnorderedElementsAre(Elem(7, -7), Elem(17, -17), Elem(19, -190))); EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70))); auto node = set1.extract(7); EXPECT_TRUE(node); EXPECT_EQ(node.key().i, 7); EXPECT_EQ(node.mapped(), -7); EXPECT_THAT(set1, UnorderedElementsAre(Elem(17, -17), Elem(19, -190))); auto insert_result = set2.insert(std::move(node)); EXPECT_FALSE(node); EXPECT_FALSE(insert_result.inserted); EXPECT_TRUE(insert_result.node); EXPECT_EQ(insert_result.node.key().i, 7); EXPECT_EQ(insert_result.node.mapped(), -7); EXPECT_THAT(*insert_result.position, Elem(7, -70)); EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70))); node = set1.extract(17); EXPECT_TRUE(node); EXPECT_EQ(node.key().i, 17); EXPECT_EQ(node.mapped(), -17); EXPECT_THAT(set1, UnorderedElementsAre(Elem(19, -190))); node.mapped() = 23; insert_result = set2.insert(std::move(node)); EXPECT_FALSE(node); EXPECT_TRUE(insert_result.inserted); EXPECT_FALSE(insert_result.node); EXPECT_THAT(*insert_result.position, Elem(17, 23)); EXPECT_THAT(set2, UnorderedElementsAre(Elem(7, -70), Elem(17, 23))); } } // namespace } // namespace container_internal } // namespace absl