| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
- // Exceptions. See /LICENSE for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- #include "common/set.h"
- #include <gmock/gmock.h>
- #include <gtest/gtest.h>
- #include <initializer_list>
- #include <type_traits>
- #include <vector>
- #include "common/raw_hashtable_test_helpers.h"
- namespace Carbon {
- namespace {
- using RawHashtable::IndexKeyContext;
- using RawHashtable::MoveOnlyTestData;
- using RawHashtable::TestData;
- using ::testing::UnorderedElementsAreArray;
- template <typename SetT, typename MatcherRangeT>
- auto ExpectSetElementsAre(SetT&& s, MatcherRangeT element_matchers) -> void {
- // Collect the elements into a container.
- using KeyT = typename std::remove_reference<SetT>::type::KeyT;
- std::vector<std::reference_wrapper<KeyT>> entries;
- s.ForEach([&entries](KeyT& k) { entries.push_back(std::ref(k)); });
- // Use the GoogleMock unordered container matcher to validate and show errors
- // on wrong elements.
- EXPECT_THAT(entries, UnorderedElementsAreArray(element_matchers));
- }
- // Allow directly using an initializer list.
- template <typename SetT, typename MatcherT>
- auto ExpectSetElementsAre(SetT&& s,
- std::initializer_list<MatcherT> element_matchers)
- -> void {
- std::vector<MatcherT> element_matchers_storage = element_matchers;
- ExpectSetElementsAre(s, element_matchers_storage);
- }
- template <typename RangeT, typename... RangeTs>
- auto MakeElements(RangeT&& range, RangeTs&&... ranges) {
- std::vector<typename RangeT::value_type> elements;
- auto add_range = [&elements](RangeT&& r) {
- for (const auto&& e : r) {
- elements.push_back(e);
- }
- };
- add_range(std::forward<RangeT>(range));
- (add_range(std::forward<RangeT>(ranges)), ...);
- return elements;
- }
- template <typename SetT>
- class SetTest : public ::testing::Test {};
- template <typename SetT>
- class MoveOnlySetTest : public ::testing::Test {};
- using Types = ::testing::Types<Set<int>, Set<int, 16>, Set<int, 128>,
- Set<TestData>, Set<TestData, 16>>;
- TYPED_TEST_SUITE(SetTest, Types);
- using MoveOnlyTypes =
- ::testing::Types<Set<MoveOnlyTestData>, Set<MoveOnlyTestData, 16>,
- Set<MoveOnlyTestData, 64>>;
- TYPED_TEST_SUITE(MoveOnlySetTest, MoveOnlyTypes);
- TYPED_TEST(SetTest, Basic) {
- using SetT = TypeParam;
- SetT s;
- EXPECT_FALSE(s.Contains(42));
- EXPECT_TRUE(s.Insert(1).is_inserted());
- EXPECT_TRUE(s.Contains(1));
- auto result = s.Lookup(1);
- EXPECT_TRUE(result);
- EXPECT_EQ(1, result.key());
- auto i_result = s.Insert(1);
- EXPECT_FALSE(i_result.is_inserted());
- EXPECT_TRUE(s.Contains(1));
- // Verify all the elements.
- ExpectSetElementsAre(s, {1});
- // Fill up a bunch to ensure we trigger growth a few times.
- for (int i : llvm::seq(2, 512)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- EXPECT_TRUE(s.Insert(i).is_inserted());
- }
- for (int i : llvm::seq(1, 512)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- EXPECT_TRUE(s.Contains(i));
- EXPECT_FALSE(s.Insert(i).is_inserted());
- }
- EXPECT_FALSE(s.Contains(513));
- // Verify all the elements.
- ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 512)));
- }
- TYPED_TEST(SetTest, FactoryApi) {
- using SetT = TypeParam;
- SetT s;
- EXPECT_TRUE(s.Insert(1, [](int k, void* key_storage) {
- return new (key_storage) int(k);
- }).is_inserted());
- ASSERT_TRUE(s.Contains(1));
- // Reinsertion doesn't invoke the callback.
- EXPECT_FALSE(s.Insert(1, [](int, void*) -> int* {
- llvm_unreachable("Should never be called!");
- }).is_inserted());
- }
- TYPED_TEST(SetTest, Copy) {
- using SetT = TypeParam;
- SetT s;
- // Make sure we exceed the small size for some of the set types, but not all
- // of them, so we cover all the combinations of copying between small and
- // large.
- for (int i : llvm::seq(1, 24)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Insert(i).is_inserted());
- }
- SetT other_s1 = s;
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 24)));
- // Add some more elements to the original.
- for (int i : llvm::seq(24, 32)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Insert(i).is_inserted());
- }
- // The first copy doesn't change.
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 24)));
- // A new copy does.
- SetT other_s2 = s;
- ExpectSetElementsAre(other_s2, MakeElements(llvm::seq(1, 32)));
- // Copy-assign updates.
- other_s1 = s;
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32)));
- // Self-assign is a no-op.
- other_s1 = const_cast<const SetT&>(other_s1);
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32)));
- // But mutating original still doesn't change copies.
- for (int i : llvm::seq(32, 48)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Insert(i).is_inserted());
- }
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32)));
- ExpectSetElementsAre(other_s2, MakeElements(llvm::seq(1, 32)));
- }
- TYPED_TEST(SetTest, Move) {
- using SetT = TypeParam;
- SetT s;
- // Make sure we exceed the small size for some of the set types, but not all
- // of them, so we cover all the combinations of copying between small and
- // large.
- for (int i : llvm::seq(1, 24)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- EXPECT_TRUE(s.Insert(i).is_inserted());
- }
- SetT other_s1 = std::move(s);
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 24)));
- // Add some more elements.
- for (int i : llvm::seq(24, 32)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(other_s1.Insert(i).is_inserted());
- }
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32)));
- // Move back over a moved-from.
- s = std::move(other_s1);
- ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 32)));
- // Copy over moved-from state also works.
- other_s1 = s;
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32)));
- // Now add still more elements.
- for (int i : llvm::seq(32, 48)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(other_s1.Insert(i).is_inserted());
- }
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 48)));
- // Move-assign over the copy looks like the moved-from table not the copy.
- other_s1 = std::move(s);
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32)));
- // Self-swap (which does a self-move) works and is a no-op.
- std::swap(other_s1, other_s1);
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32)));
- // Test copying of a moved-from table over a valid table and
- // self-move-assign. The former is required to be valid, and the latter is
- // in at least the case of self-move-assign-when-moved-from, but the result
- // can be in any state so just do them and ensure we don't crash.
- SetT other_s2 = other_s1;
- // NOLINTNEXTLINE(bugprone-use-after-move): Testing required use-after-move.
- other_s2 = s;
- other_s1 = std::move(other_s1);
- s = std::move(s);
- }
- TYPED_TEST(MoveOnlySetTest, Move) {
- using SetT = TypeParam;
- static_assert(!std::is_copy_assignable_v<SetT>);
- static_assert(!std::is_copy_constructible_v<SetT>);
- static_assert(std::is_move_assignable_v<SetT>);
- static_assert(std::is_move_constructible_v<SetT>);
- auto make_set = [] {
- SetT s;
- // Make sure we exceed the small size for some of the set types, but not all
- // of them, so we cover all the combinations of copying between small and
- // large.
- for (int i : llvm::seq(1, 24)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- EXPECT_TRUE(s.Insert(i).is_inserted());
- }
- return s;
- };
- SetT s = make_set();
- SetT other_s1 = std::move(s);
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 24)));
- // Add some more elements.
- for (int i : llvm::seq(24, 32)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(other_s1.Insert(i).is_inserted());
- }
- ExpectSetElementsAre(other_s1, MakeElements(llvm::seq(1, 32)));
- // Move back over a moved-from.
- s = std::move(other_s1);
- ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 32)));
- // Now add still more elements, crossing the small size limit for all tested
- // map types.
- for (int i : llvm::seq(32, 72)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Insert(i).is_inserted());
- }
- ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 72)));
- // Assignment replaces the contents.
- s = make_set();
- ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 24)));
- // Self-swap (which does a self-move) works and is a no-op.
- std::swap(s, s);
- ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 24)));
- }
- TYPED_TEST(SetTest, Conversions) {
- using SetT = TypeParam;
- using KeyT = SetT::KeyT;
- SetT s;
- ASSERT_TRUE(s.Insert(1).is_inserted());
- ASSERT_TRUE(s.Insert(2).is_inserted());
- ASSERT_TRUE(s.Insert(3).is_inserted());
- ASSERT_TRUE(s.Insert(4).is_inserted());
- SetView<KeyT> sv = s;
- SetView<const KeyT> csv = sv;
- SetView<const KeyT> csv2 = s;
- EXPECT_TRUE(sv.Contains(1));
- EXPECT_TRUE(csv.Contains(2));
- EXPECT_TRUE(csv2.Contains(3));
- }
- TYPED_TEST(SetTest, GrowToAllocSize) {
- using SetT = TypeParam;
- SetT s;
- // Grow when empty. May be a no-op for some small sizes.
- s.GrowToAllocSize(32);
- // Add some elements that will need to be propagated through subsequent
- // growths. Also delete some.
- ssize_t storage_bytes = s.ComputeMetrics().storage_bytes;
- for (int i : llvm::seq(1, 24)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Insert(i).is_inserted());
- }
- for (int i : llvm::seq(1, 8)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Erase(i));
- }
- // No further growth triggered.
- EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes);
- // No-op.
- s.GrowToAllocSize(16);
- ExpectSetElementsAre(s, MakeElements(llvm::seq(8, 24)));
- // No further growth triggered.
- EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes);
- // Get a few doubling based growths, and at least one beyond the largest small
- // size.
- s.GrowToAllocSize(64);
- ExpectSetElementsAre(s, MakeElements(llvm::seq(8, 24)));
- s.GrowToAllocSize(128);
- ExpectSetElementsAre(s, MakeElements(llvm::seq(8, 24)));
- s.GrowToAllocSize(256);
- ExpectSetElementsAre(s, MakeElements(llvm::seq(8, 24)));
- // Update the storage bytes after growth.
- EXPECT_LT(storage_bytes, s.ComputeMetrics().storage_bytes);
- storage_bytes = s.ComputeMetrics().storage_bytes;
- // Add some more, but not enough to trigger further growth, and then grow by
- // several more multiples of two to test handling large growth.
- for (int i : llvm::seq(24, 48)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Insert(i).is_inserted());
- }
- for (int i : llvm::seq(8, 16)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Erase(i));
- }
- // No growth from insertions.
- EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes);
- s.GrowToAllocSize(1024);
- ExpectSetElementsAre(s, MakeElements(llvm::seq(16, 48)));
- // Storage should have grown.
- EXPECT_LT(storage_bytes, s.ComputeMetrics().storage_bytes);
- }
- TYPED_TEST(SetTest, GrowForInsert) {
- using SetT = TypeParam;
- SetT s;
- s.GrowForInsertCount(42);
- ssize_t storage_bytes = s.ComputeMetrics().storage_bytes;
- for (int i : llvm::seq(1, 42)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Insert(i).is_inserted());
- }
- ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 42)));
- EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes);
- // Erase many elements and grow again for another insert.
- for (int i : llvm::seq(1, 32)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Erase(i));
- }
- s.GrowForInsertCount(42);
- storage_bytes = s.ComputeMetrics().storage_bytes;
- for (int i : llvm::seq(42, 84)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Insert(i).is_inserted());
- }
- ExpectSetElementsAre(s, MakeElements(llvm::seq(32, 84)));
- EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes);
- // Erase all the elements, then grow for a much larger insertion and insert
- // again.
- for (int i : llvm::seq(32, 84)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Erase(i));
- }
- s.GrowForInsertCount(321);
- storage_bytes = s.ComputeMetrics().storage_bytes;
- for (int i : llvm::seq(128, 321 + 128)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- ASSERT_TRUE(s.Insert(i).is_inserted());
- }
- ExpectSetElementsAre(s, MakeElements(llvm::seq(128, 321 + 128)));
- EXPECT_EQ(storage_bytes, s.ComputeMetrics().storage_bytes);
- }
- TEST(SetContextTest, Basic) {
- llvm::SmallVector<TestData> keys;
- for (int i : llvm::seq(0, 513)) {
- keys.push_back(i * 100);
- }
- IndexKeyContext<TestData> key_context(keys);
- Set<ssize_t, 0, IndexKeyContext<TestData>> s;
- EXPECT_FALSE(s.Contains(42, key_context));
- EXPECT_TRUE(s.Insert(1, key_context).is_inserted());
- EXPECT_TRUE(s.Contains(1, key_context));
- auto result = s.Lookup(TestData(100), key_context);
- EXPECT_TRUE(result);
- EXPECT_EQ(1, result.key());
- auto i_result = s.Insert(1, IndexKeyContext<TestData>(keys));
- EXPECT_FALSE(i_result.is_inserted());
- EXPECT_TRUE(s.Contains(1, key_context));
- EXPECT_TRUE(
- s.Insert(TestData(200), [] { return 2; }, key_context).is_inserted());
- EXPECT_TRUE(s.Contains(2, key_context));
- EXPECT_TRUE(s.Contains(TestData(200), key_context));
- // Verify all the elements.
- ExpectSetElementsAre(s, {1, 2});
- // Fill up a bunch to ensure we trigger growth a few times.
- for (int i : llvm::seq(3, 512)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- EXPECT_TRUE(s.Insert(i, key_context).is_inserted());
- }
- for (int i : llvm::seq(1, 512)) {
- SCOPED_TRACE(llvm::formatv("Key: {0}", i).str());
- EXPECT_TRUE(s.Contains(i, key_context));
- EXPECT_FALSE(s.Insert(i, key_context).is_inserted());
- }
- EXPECT_FALSE(s.Contains(0, key_context));
- EXPECT_FALSE(s.Contains(512, key_context));
- EXPECT_FALSE(s.Contains(TestData(0), key_context));
- EXPECT_FALSE(s.Contains(TestData(51200), key_context));
- // Verify all the elements.
- ExpectSetElementsAre(s, MakeElements(llvm::seq(1, 512)));
- }
- } // namespace
- } // namespace Carbon
|