xref: /aosp_15_r20/external/cronet/base/containers/intrusive_heap_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/containers/intrusive_heap.h"
6 
7 #include "base/check_op.h"
8 #include "base/functional/callback_helpers.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/memory/raw_ptr.h"
11 #include "base/notreached.h"
12 #include "base/rand_util.h"
13 #include "base/test/bind.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace base {
18 
19 namespace {
20 
21 using IntrusiveHeapInt = IntrusiveHeap<WithHeapHandle<int>>;
22 
23 // Validates whether or not the given heap satisfies the heap invariant.
24 template <class H>
ExpectHeap(const H & heap)25 void ExpectHeap(const H& heap) {
26   const auto& less = heap.value_comp();
27   const auto& handle_access = heap.heap_handle_access();
28 
29   for (size_t i = 0; i < heap.size(); ++i) {
30     size_t left = intrusive_heap::LeftIndex(i);
31     size_t right = left + 1;
32 
33     if (left < heap.size())
34       EXPECT_FALSE(less(heap[i], heap[left]));
35     if (right < heap.size())
36       EXPECT_FALSE(less(heap[i], heap[right]));
37 
38     intrusive_heap::CheckInvalidOrEqualTo(handle_access.GetHeapHandle(&heap[i]),
39                                           i);
40   }
41 }
42 
43 // A small set of canonical elements, and the a function for validating the
44 // heap that should be created by those elements. This is used in various
45 // constructor/insertion tests.
46 #define CANONICAL_ELEMENTS 3, 1, 2, 4, 5, 6, 7, 0
ExpectCanonical(const IntrusiveHeapInt & heap)47 void ExpectCanonical(const IntrusiveHeapInt& heap) {
48   ExpectHeap(heap);
49 
50   // Manual implementation of a max-heap inserting the elements defined by
51   // CANONICAL_ELEMENTS:
52   // 3
53   // 3 1
54   // 3 1 2
55   // 3 1 2 4 -> 3 4 2 1 -> 4 3 2 1
56   // 4 3 2 1 5 -> 4 5 2 1 3 -> 5 4 2 1 3
57   // 5 4 2 1 3 6 -> 5 4 6 1 3 2 -> 6 4 5 1 3 2
58   // 6 4 5 1 3 2 7 -> 6 4 7 1 3 2 5 -> 7 4 6 1 3 2 5
59   // 7 4 6 1 3 2 5 0
60   std::vector<int> expected{7, 4, 6, 1, 3, 2, 5, 0};
61   std::vector<int> actual;
62   for (const auto& element : heap)
63     actual.push_back(element.value());
64   ASSERT_THAT(actual, testing::ContainerEq(expected));
65 }
66 
67 // Initializes the given heap to be the "canonical" heap from the point of view
68 // of these tests.
MakeCanonical(IntrusiveHeapInt * heap)69 void MakeCanonical(IntrusiveHeapInt* heap) {
70   static constexpr int kInts[] = {CANONICAL_ELEMENTS};
71   heap->clear();
72   heap->insert(kInts, kInts + std::size(kInts));
73   ExpectCanonical(*heap);
74 }
75 
76 // A handful of helper functions and classes related to building an exhaustive
77 // stress test for IntrusiveHeap, with all combinations of default-constructible
78 // supports-move-operations and supports-copy-operations value types.
79 
80 // IntrusiveHeap supports 3 types of operations: those that cause the heap to
81 // get smaller (deletions), those that keep the heap the same size (updates,
82 // replaces, etc), and those that cause the heap to get bigger (insertions).
83 enum OperationTypes : int {
84   kGrowing,
85   kShrinking,
86   kSameSize,
87   kOperationTypesCount
88 };
89 
90 // The operations that cause a heap to get bigger.
91 enum GrowingOperations : int { kInsert, kEmplace, kGrowingOperationsCount };
92 
93 // The operations that cause a heap to get smaller. Some of these are only
94 // supported by move-only value types.
95 enum ShrinkingOperations : int {
96   kTake,
97   kTakeTop,
98   kErase,
99   kPop,
100   kShrinkingOperationsCount
101 };
102 
103 // The operations that keep a heap the same size.
104 enum SameSizeOperations : int {
105   kReplace,
106   kReplaceTop,
107   kUpdate,
108   kSameSizeOperationsCount
109 };
110 
111 // Randomly selects an operation for the GrowingOperations enum, applies it to
112 // the given heap, and validates that the operation completed as expected.
113 template <typename T>
DoGrowingOperation(IntrusiveHeap<T> * heap)114 void DoGrowingOperation(IntrusiveHeap<T>* heap) {
115   GrowingOperations op = static_cast<GrowingOperations>(
116       base::RandInt(0, kGrowingOperationsCount - 1));
117 
118   int value = base::RandInt(0, 1000);
119   size_t old_size = heap->size();
120   typename IntrusiveHeap<T>::const_iterator it;
121 
122   switch (op) {
123     case kInsert: {
124       it = heap->insert(T(value));
125       break;
126     }
127 
128     case kEmplace: {
129       it = heap->emplace(value);
130       break;
131     }
132 
133     case kGrowingOperationsCount:
134       NOTREACHED();
135   }
136 
137   EXPECT_EQ(old_size + 1, heap->size());
138   EXPECT_EQ(value, it->value());
139   EXPECT_EQ(it->GetHeapHandle().index(), heap->ToIndex(it));
140 }
141 
142 // Helper struct for determining with the given value type T is movable or not.
143 // Used to determine whether or not the "take" operations can be used.
144 template <typename T>
145 struct NotMovable {
146   static constexpr bool value = !std::is_nothrow_move_constructible_v<T> &&
147                                 std::is_copy_constructible_v<T>;
148 };
149 
150 // Invokes "take" if the type is movable, otherwise invokes erase.
151 template <typename T, bool kNotMovable = NotMovable<T>::value>
152 struct Take;
153 template <typename T>
154 struct Take<T, true> {
Dobase::__anon39cfb4090111::Take155   static void Do(IntrusiveHeap<T>* heap, size_t index) { heap->erase(index); }
156 };
157 template <typename T>
158 struct Take<T, false> {
Dobase::__anon39cfb4090111::Take159   static void Do(IntrusiveHeap<T>* heap, size_t index) {
160     int value = heap->at(index).value();
161     T t = heap->take(index);
162     EXPECT_EQ(value, t.value());
163     EXPECT_FALSE(t.GetHeapHandle().IsValid());
164   }
165 };
166 
167 // Invokes "take_top" if the type is movable, otherwise invokes pop.
168 template <typename T, bool kNotMovable = NotMovable<T>::value>
169 struct TakeTop;
170 template <typename T>
171 struct TakeTop<T, true> {
Dobase::__anon39cfb4090111::TakeTop172   static void Do(IntrusiveHeap<T>* heap) { heap->pop(); }
173 };
174 template <typename T>
175 struct TakeTop<T, false> {
Dobase::__anon39cfb4090111::TakeTop176   static void Do(IntrusiveHeap<T>* heap) {
177     int value = heap->at(0).value();
178     T t = heap->take_top();
179     EXPECT_EQ(value, t.value());
180     EXPECT_FALSE(t.GetHeapHandle().IsValid());
181   }
182 };
183 
184 // Randomly selects a shrinking operations, applies it to the given |heap| and
185 // validates that the operation completed as expected and resulted in a valid
186 // heap. The "take" operations will only be invoked for a value type T that
187 // supports move operations, otherwise they will be mapped to erase/pop.
188 template <typename T>
DoShrinkingOperation(IntrusiveHeap<T> * heap)189 void DoShrinkingOperation(IntrusiveHeap<T>* heap) {
190   ShrinkingOperations op = static_cast<ShrinkingOperations>(
191       base::RandInt(0, kShrinkingOperationsCount - 1));
192 
193   size_t old_size = heap->size();
194   size_t index = static_cast<size_t>(base::RandInt(0, old_size - 1));
195 
196   switch (op) {
197     case kTake: {
198       Take<T>::Do(heap, index);
199       break;
200     }
201 
202     case kTakeTop: {
203       TakeTop<T>::Do(heap);
204       break;
205     }
206 
207     case kErase: {
208       heap->erase(index);
209       break;
210     }
211 
212     case kPop: {
213       heap->pop();
214       break;
215     }
216 
217     case kShrinkingOperationsCount:
218       NOTREACHED();
219   }
220 
221   EXPECT_EQ(old_size - 1, heap->size());
222 }
223 
224 // Randomly selects a same size operation, applies it to the given |heap| and
225 // validates that the operation completed as expected and resulted in a valid
226 // heap.
227 template <typename T>
DoSameSizeOperation(IntrusiveHeap<T> * heap)228 void DoSameSizeOperation(IntrusiveHeap<T>* heap) {
229   SameSizeOperations op = static_cast<SameSizeOperations>(
230       base::RandInt(0, kSameSizeOperationsCount - 1));
231 
232   size_t old_size = heap->size();
233   size_t index = static_cast<size_t>(base::RandInt(0, old_size - 1));
234   if (op == kReplaceTop)
235     index = 0;
236   int new_value = base::RandInt(0, 1000);
237   typename IntrusiveHeap<T>::const_iterator it;
238 
239   switch (op) {
240     case kReplace: {
241       it = heap->Replace(index, T(new_value));
242       break;
243     }
244 
245     case kReplaceTop: {
246       it = heap->ReplaceTop(T(new_value));
247       break;
248     }
249 
250     case kUpdate: {
251       it = heap->Modify(
252           index, [&new_value](T& element) { element.set_value(new_value); });
253       break;
254     }
255 
256     case kSameSizeOperationsCount:
257       NOTREACHED();
258   }
259 
260   EXPECT_EQ(old_size, heap->size());
261   EXPECT_EQ(new_value, it->value());
262   EXPECT_EQ(it->GetHeapHandle().index(), heap->ToIndex(it));
263 }
264 
265 // Randomly selects an operation, applies it to the given |heap| and validates
266 // that the operation completed as expected and resulted in a valid heap.
267 template <typename T>
DoRandomHeapOperation(IntrusiveHeap<T> * heap)268 void DoRandomHeapOperation(IntrusiveHeap<T>* heap) {
269   static constexpr int kMinHeapSize = 10u;
270   static constexpr int kMaxHeapSize = 100u;
271 
272   OperationTypes operation_type =
273       static_cast<OperationTypes>(base::RandInt(0, kOperationTypesCount - 1));
274 
275   // Keep the heap size bounded by forcing growing and shrinking operations when
276   // it exceeds the bounds.
277   if (heap->size() < kMinHeapSize) {
278     operation_type = kGrowing;
279   } else if (heap->size() > kMaxHeapSize) {
280     operation_type = kShrinking;
281   }
282 
283   switch (operation_type) {
284     case kGrowing:
285       DoGrowingOperation(heap);
286       break;
287     case kShrinking:
288       DoShrinkingOperation(heap);
289       break;
290     case kSameSize:
291       DoSameSizeOperation(heap);
292       break;
293     case kOperationTypesCount:
294       NOTREACHED();
295   }
296 }
297 
298 // A stress test that is only applicable to value types T that support move
299 // operations.
300 template <typename T>
MoveStressTest()301 void MoveStressTest() {
302   IntrusiveHeap<T> heap({2, 4, 6, 8});
303   EXPECT_EQ(4u, heap.size());
304   EXPECT_FALSE(heap.empty());
305   ExpectHeap(heap);
306 
307   IntrusiveHeap<T> heap2(std::move(heap));
308   EXPECT_EQ(4u, heap2.size());
309   EXPECT_FALSE(heap2.empty());
310   ExpectHeap(heap2);
311   EXPECT_EQ(0u, heap.size());
312   EXPECT_TRUE(heap.empty());
313   ExpectHeap(heap);
314 
315   heap = std::move(heap2);
316   EXPECT_EQ(4u, heap.size());
317   EXPECT_FALSE(heap.empty());
318   ExpectHeap(heap);
319   EXPECT_EQ(0u, heap2.size());
320   EXPECT_TRUE(heap2.empty());
321   ExpectHeap(heap2);
322 }
323 
324 // A stress that that is only applicable to value types T that support copy
325 // operations.
326 template <typename T>
CopyStressTest()327 void CopyStressTest() {
328   IntrusiveHeap<T> heap({2, 4, 6, 8});
329   EXPECT_EQ(4u, heap.size());
330   EXPECT_FALSE(heap.empty());
331   ExpectHeap(heap);
332 
333   IntrusiveHeap<T> heap2(heap);
334   EXPECT_EQ(4u, heap2.size());
335   EXPECT_FALSE(heap2.empty());
336   ExpectHeap(heap2);
337   EXPECT_EQ(4u, heap.size());
338   EXPECT_FALSE(heap.empty());
339   ExpectHeap(heap);
340 
341   IntrusiveHeap<T> heap3({1, 3, 5});
342   heap3.clear();
343   heap3 = heap;
344   EXPECT_EQ(4u, heap3.size());
345   EXPECT_FALSE(heap3.empty());
346   ExpectHeap(heap);
347   EXPECT_EQ(4u, heap.size());
348   EXPECT_FALSE(heap.empty());
349   ExpectHeap(heap);
350 
351   EXPECT_TRUE(heap == heap2);
352   EXPECT_FALSE(heap != heap2);
353 }
354 
355 // A stress test that is applicable to all value types, whether or not they
356 // support copy and/or move operations.
357 template <typename T>
GeneralStressTest()358 void GeneralStressTest() {
359   std::vector<int> vector{2, 4, 6, 8};
360   IntrusiveHeap<T> heap(vector.begin(), vector.end());
361   EXPECT_EQ(4u, heap.size());
362   EXPECT_FALSE(heap.empty());
363   ExpectHeap(heap);
364 
365   heap.clear();
366   EXPECT_EQ(0u, heap.size());
367   EXPECT_TRUE(heap.empty());
368   ExpectHeap(heap);
369 
370   // Create an element and get a handle to it.
371   auto it = heap.insert(T(34));
372   EXPECT_EQ(1u, heap.size());
373   HeapHandle* handle = it->handle();
374   EXPECT_EQ(0u, handle->index());
375   ExpectHeap(heap);
376 
377   // Add some other elements.
378   heap.insert(T(12));
379   heap.emplace(14);
380   EXPECT_EQ(3u, heap.size());
381   ExpectHeap(heap);
382 
383   // The handle should have tracked the element it is associated with.
384   EXPECT_EQ(34, heap[*handle].value());
385 
386   // Replace with a value that shouldn't need moving in the heap.
387   size_t index = handle->index();
388   handle = heap.Replace(*handle, T(40))->handle();
389   EXPECT_EQ(3u, heap.size());
390   ExpectHeap(heap);
391   EXPECT_EQ(index, handle->index());
392 
393   // Replace with a value that should cause the entry to move.
394   handle = heap.Replace(handle->index(), T(1))->handle();
395   EXPECT_EQ(3u, heap.size());
396   ExpectHeap(heap);
397   EXPECT_NE(index, handle->index());
398 
399   // Replace the top element.
400   heap.ReplaceTop(T(65));
401   EXPECT_EQ(3u, heap.size());
402   ExpectHeap(heap);
403 
404   // Insert several more elements.
405   std::vector<int> elements({13, 17, 19, 23, 29, 31, 37, 41});
406   heap.insert(elements.begin(), elements.end());
407   EXPECT_EQ(11u, heap.size());
408   ExpectHeap(heap);
409 
410   // Invasively change an element that is already inside the heap, and then
411   // repair the heap.
412   T* element = const_cast<T*>(&heap[7]);
413   element->set_value(97);
414   heap.Update(7u);
415   ExpectHeap(heap);
416 
417   // Safely modify an element that is already inside the heap.
418   heap.Modify(7u, [](T& element) { element.set_value(128); });
419   ExpectHeap(heap);
420 
421   // Do some more updates that are no-ops, just to explore all the flavours of
422   // ToIndex.
423   handle = heap[5].handle();
424   heap.Update(*handle);
425   heap.Update(heap.begin() + 6);
426   heap.Update(heap.rbegin() + 8);
427   ExpectHeap(heap);
428 
429   handle = heap[5].handle();
430   EXPECT_TRUE(handle);
431   EXPECT_EQ(5u, handle->index());
432   EXPECT_EQ(5u, heap.ToIndex(*handle));
433   EXPECT_EQ(5u, heap.ToIndex(heap.begin() + 5));
434   EXPECT_EQ(5u, heap.ToIndex(heap.cbegin() + 5));
435   EXPECT_EQ(5u, heap.ToIndex(heap.rbegin() + 5));
436   EXPECT_EQ(5u, heap.ToIndex(heap.crbegin() + 5));
437   EXPECT_EQ(HeapHandle::kInvalidIndex, heap.ToIndex(heap.end()));
438   EXPECT_EQ(HeapHandle::kInvalidIndex, heap.ToIndex(heap.cend()));
439   EXPECT_EQ(HeapHandle::kInvalidIndex, heap.ToIndex(heap.rend()));
440   EXPECT_EQ(HeapHandle::kInvalidIndex, heap.ToIndex(heap.crend()));
441 
442   EXPECT_EQ(&heap[0], &heap.at(0));
443   EXPECT_EQ(&heap[0], &heap.front());
444   EXPECT_EQ(&heap[0], &heap.top());
445   EXPECT_EQ(&heap[heap.size() - 1], &heap.back());
446   EXPECT_EQ(&heap[0], heap.data());
447 
448   // Do a bunch of random operations on a heap as a stress test.
449   for (size_t i = 0; i < 1000; ++i) {
450     DoRandomHeapOperation(&heap);
451     ExpectHeap(heap);
452   }
453 }
454 
455 // A basic value type that wraps an integer. This is default constructible, and
456 // supports both move and copy operations.
457 class Value : public InternalHeapHandleStorage {
458  public:
Value(int value)459   explicit Value(int value) : value_(value) {}
Value()460   Value() : value_(-1) {}
Value(Value && other)461   Value(Value&& other) noexcept
462       : InternalHeapHandleStorage(std::move(other)),
463         value_(std::exchange(other.value_, -1)) {}
Value(const Value & other)464   Value(const Value& other) : value_(other.value_) {
465     HeapHandle h = other.GetHeapHandle();
466     if (h.IsValid())
467       SetHeapHandle(h);
468   }
~Value()469   ~Value() override {}
470 
operator =(Value && other)471   Value& operator=(Value&& other) noexcept {
472     InternalHeapHandleStorage::operator=(std::move(other));
473     value_ = std::exchange(other.value_, -1);
474     return *this;
475   }
operator =(const Value & other)476   Value& operator=(const Value& other) {
477     value_ = other.value_;
478     return *this;
479   }
480 
value() const481   int value() const { return value_; }
set_value(int value)482   void set_value(int value) { value_ = value; }
483 
operator ==(const Value & rhs) const484   bool operator==(const Value& rhs) const { return value_ == rhs.value_; }
operator !=(const Value & rhs) const485   bool operator!=(const Value& rhs) const { return value_ != rhs.value_; }
operator <=(const Value & rhs) const486   bool operator<=(const Value& rhs) const { return value_ <= rhs.value_; }
operator >=(const Value & rhs) const487   bool operator>=(const Value& rhs) const { return value_ >= rhs.value_; }
operator <(const Value & rhs) const488   bool operator<(const Value& rhs) const { return value_ < rhs.value_; }
operator >(const Value & rhs) const489   bool operator>(const Value& rhs) const { return value_ > rhs.value_; }
490 
491  private:
492   int value_;
493 };
494 
495 // Macro for creating versions of Value that selectively enable/disable
496 // default-constructors, move-operations and copy-operations.
497 #define DEFINE_VALUE_TYPE(name, default_construct, move, copy) \
498   class name : public Value {                                  \
499    public:                                                     \
500     explicit name(int value) : Value(value) {}                 \
501     name() = default_construct;                                \
502     name(name&&) noexcept = move;                              \
503     name(const name&) = copy;                                  \
504     name& operator=(name&&) noexcept = move;                   \
505     name& operator=(const name&) = copy;                       \
506   };
507 
DEFINE_VALUE_TYPE(Value_DMC,default,default,default)508 DEFINE_VALUE_TYPE(Value_DMC, default, default, default)
509 DEFINE_VALUE_TYPE(Value_DmC, default, delete, default)
510 DEFINE_VALUE_TYPE(Value_DMc, default, default, delete)
511 DEFINE_VALUE_TYPE(Value_dMC, delete, default, default)
512 DEFINE_VALUE_TYPE(Value_dmC, delete, delete, default)
513 DEFINE_VALUE_TYPE(Value_dMc, delete, default, delete)
514 
515 // Used to validate that the generated value types work as expected wrt
516 // default-constructors, move-operations and copy-operations.
517 template <typename ValueType, bool D, bool M, bool C>
518 void ValidateValueType() {
519   static_assert(std::is_default_constructible_v<ValueType> == D, "oops");
520   static_assert(std::is_move_constructible_v<ValueType> == M, "oops");
521   static_assert(std::is_move_assignable_v<ValueType> == M, "oops");
522   static_assert(std::is_copy_constructible_v<ValueType> == C, "oops");
523   static_assert(std::is_copy_assignable_v<ValueType> == C, "oops");
524 }
525 
526 // A small test element that provides its own HeapHandle storage and implements
527 // the contract expected of the DefaultHeapHandleAccessor.
528 struct TestElement {
529   int key;
530   raw_ptr<HeapHandle> handle;
531 
532   // Make this a min-heap by return > instead of <.
operator <base::__anon39cfb4090111::TestElement533   bool operator<(const TestElement& other) const { return key > other.key; }
534 
SetHeapHandlebase::__anon39cfb4090111::TestElement535   void SetHeapHandle(HeapHandle h) {
536     if (handle)
537       *handle = h;
538   }
539 
ClearHeapHandlebase::__anon39cfb4090111::TestElement540   void ClearHeapHandle() {
541     if (handle)
542       handle->reset();
543   }
544 
GetHeapHandlebase::__anon39cfb4090111::TestElement545   HeapHandle GetHeapHandle() const {
546     if (handle)
547       return *handle;
548     return HeapHandle::Invalid();
549   }
550 };
551 
552 }  // namespace
553 
554 ////////////////////////////////////////////////////////////////////////////////
555 // TEST SUITE 1
556 //
557 // Explicit tests of a simple heap using WithHeapHandle<>.
558 
TEST(IntrusiveHeapTest,Constructors)559 TEST(IntrusiveHeapTest, Constructors) {
560   {
561     // Default constructor.
562     IntrusiveHeapInt heap;
563     EXPECT_TRUE(heap.empty());
564   }
565 
566   {
567     // Constructor with iterators.
568     std::vector<int> ints{CANONICAL_ELEMENTS};
569     IntrusiveHeapInt heap(ints.begin(), ints.end());
570     ExpectCanonical(heap);
571 
572     // Move constructor.
573     IntrusiveHeapInt heap2(std::move(heap));
574     EXPECT_TRUE(heap.empty());
575     ExpectCanonical(heap2);
576   }
577 
578   {
579     // Constructor with initializer list.
580     IntrusiveHeapInt heap{CANONICAL_ELEMENTS};
581     ExpectCanonical(heap);
582   }
583 }
584 
TEST(IntrusiveHeapTest,Assignment)585 TEST(IntrusiveHeapTest, Assignment) {
586   IntrusiveHeapInt heap{CANONICAL_ELEMENTS};
587 
588   // Move assignment.
589   IntrusiveHeapInt heap2;
590   heap2 = std::move(heap);
591   EXPECT_TRUE(heap.empty());
592   ExpectCanonical(heap2);
593 }
594 
TEST(IntrusiveHeapTest,Swap)595 TEST(IntrusiveHeapTest, Swap) {
596   IntrusiveHeapInt heap{CANONICAL_ELEMENTS};
597   IntrusiveHeapInt heap2;
598   swap(heap, heap2);
599   EXPECT_TRUE(heap.empty());
600   ExpectCanonical(heap2);
601   heap.swap(heap2);
602   EXPECT_TRUE(heap2.empty());
603   ExpectCanonical(heap);
604 }
605 
TEST(IntrusiveHeapTest,ElementAccess)606 TEST(IntrusiveHeapTest, ElementAccess) {
607   IntrusiveHeapInt heap{CANONICAL_ELEMENTS};
608   EXPECT_EQ(heap.front(), heap[0]);
609   EXPECT_EQ(heap.back(), heap[7]);
610   EXPECT_EQ(heap.top(), heap[0]);
611   for (size_t i = 0; i < heap.size(); ++i) {
612     EXPECT_EQ(heap[i], heap.at(i));
613     EXPECT_EQ(heap[i], heap.data()[i]);
614   }
615 }
616 
TEST(IntrusiveHeapTest,SizeManagement)617 TEST(IntrusiveHeapTest, SizeManagement) {
618   IntrusiveHeapInt heap;
619   EXPECT_TRUE(heap.empty());
620   EXPECT_LE(heap.size(), heap.capacity());
621 
622   MakeCanonical(&heap);
623   EXPECT_FALSE(heap.empty());
624   EXPECT_LE(heap.size(), heap.capacity());
625 }
626 
TEST(IntrusiveHeapTest,Iterators)627 TEST(IntrusiveHeapTest, Iterators) {
628   IntrusiveHeapInt heap;
629   MakeCanonical(&heap);
630 
631   size_t i = 0;
632   for (auto it = heap.begin(); it != heap.end(); ++it) {
633     EXPECT_EQ(i, heap.ToIndex(it));
634     EXPECT_EQ(&(*it), heap.data() + i);
635     ++i;
636   }
637 
638   i = heap.size() - 1;
639   for (auto rit = heap.rbegin(); rit != heap.rend(); ++rit) {
640     EXPECT_EQ(i, heap.ToIndex(rit));
641     EXPECT_EQ(&(*rit), heap.data() + i);
642     --i;
643   }
644 }
645 
646 ////////////////////////////////////////////////////////////////////////////////
647 // TEST SUITE 2
648 //
649 // Exhaustive stress tests with different value types, exploring all
650 // possibilities of default-constrible, movable and copyable value types.
651 
TEST(IntrusiveHeapTest,MoveOnlyNoDefaultConstructorTest)652 TEST(IntrusiveHeapTest, MoveOnlyNoDefaultConstructorTest) {
653   using ValueType = Value_dMc;
654   ValidateValueType<ValueType, false, true, false>();
655   MoveStressTest<ValueType>();
656   GeneralStressTest<ValueType>();
657 }
658 
TEST(IntrusiveHeapTest,CopyOnlyNoDefaultConstructorTest)659 TEST(IntrusiveHeapTest, CopyOnlyNoDefaultConstructorTest) {
660   using ValueType = Value_dmC;
661   ValidateValueType<ValueType, false, false, true>();
662   // We cannot perform CopyStressTest nor GeneralStressTest here, because
663   // Value_dmC has deleted move constructor and assignment operator. See
664   // crbug.com/1022576.
665 }
666 
TEST(IntrusiveHeapTest,CopyAndMoveNoDefaultConstructorTest)667 TEST(IntrusiveHeapTest, CopyAndMoveNoDefaultConstructorTest) {
668   using ValueType = Value_dMC;
669   ValidateValueType<ValueType, false, true, true>();
670   CopyStressTest<ValueType>();
671   MoveStressTest<ValueType>();
672   GeneralStressTest<ValueType>();
673 }
674 
TEST(IntrusiveHeapTest,MoveOnlyWithDefaultConstructorTest)675 TEST(IntrusiveHeapTest, MoveOnlyWithDefaultConstructorTest) {
676   using ValueType = Value_DMc;
677   ValidateValueType<ValueType, true, true, false>();
678   MoveStressTest<ValueType>();
679   GeneralStressTest<ValueType>();
680 }
681 
TEST(IntrusiveHeapTest,CopyOnlyWithDefaultConstructorTest)682 TEST(IntrusiveHeapTest, CopyOnlyWithDefaultConstructorTest) {
683   using ValueType = Value_DmC;
684   ValidateValueType<ValueType, true, false, true>();
685   // We cannot perform CopyStressTest nor GeneralStressTest here, because
686   // Value_DmC has deleted move constructor and assignment operator. See
687   // crbug.com/1022576.
688 }
689 
TEST(IntrusiveHeapTest,CopyAndMoveWithDefaultConstructorTest)690 TEST(IntrusiveHeapTest, CopyAndMoveWithDefaultConstructorTest) {
691   using ValueType = Value_DMC;
692   ValidateValueType<ValueType, true, true, true>();
693   CopyStressTest<ValueType>();
694   MoveStressTest<ValueType>();
695   GeneralStressTest<ValueType>();
696 }
697 
698 ////////////////////////////////////////////////////////////////////////////////
699 // TEST SUITE 3
700 //
701 // Tests individual functions on a custom type that provides heap handle storage
702 // externally through raw pointers.
703 
TEST(IntrusiveHeapTest,Basic)704 TEST(IntrusiveHeapTest, Basic) {
705   IntrusiveHeap<TestElement> heap;
706 
707   EXPECT_TRUE(heap.empty());
708   EXPECT_EQ(0u, heap.size());
709 }
710 
TEST(IntrusiveHeapTest,Clear)711 TEST(IntrusiveHeapTest, Clear) {
712   IntrusiveHeap<TestElement> heap;
713   HeapHandle index1;
714 
715   heap.insert({11, &index1});
716   EXPECT_EQ(1u, heap.size());
717   EXPECT_TRUE(index1.IsValid());
718 
719   heap.clear();
720   EXPECT_EQ(0u, heap.size());
721   EXPECT_FALSE(index1.IsValid());
722 }
723 
TEST(IntrusiveHeapTest,Destructor)724 TEST(IntrusiveHeapTest, Destructor) {
725   HeapHandle index1;
726 
727   {
728     IntrusiveHeap<TestElement> heap;
729 
730     heap.insert({11, &index1});
731     EXPECT_EQ(1u, heap.size());
732     EXPECT_TRUE(index1.IsValid());
733   }
734 
735   EXPECT_FALSE(index1.IsValid());
736 }
737 
TEST(IntrusiveHeapTest,Min)738 TEST(IntrusiveHeapTest, Min) {
739   IntrusiveHeap<TestElement> heap;
740 
741   heap.insert({9, nullptr});
742   heap.insert({10, nullptr});
743   heap.insert({8, nullptr});
744   heap.insert({2, nullptr});
745   heap.insert({7, nullptr});
746   heap.insert({15, nullptr});
747   heap.insert({22, nullptr});
748   heap.insert({3, nullptr});
749 
750   EXPECT_FALSE(heap.empty());
751   EXPECT_EQ(8u, heap.size());
752   EXPECT_EQ(2, heap.top().key);
753 }
754 
TEST(IntrusiveHeapTest,MinDuplicates)755 TEST(IntrusiveHeapTest, MinDuplicates) {
756   IntrusiveHeap<TestElement> heap;
757 
758   heap.insert({2, nullptr});
759   heap.insert({2, nullptr});
760   heap.insert({3, nullptr});
761 
762   EXPECT_FALSE(heap.empty());
763   EXPECT_EQ(3u, heap.size());
764   EXPECT_EQ(2, heap.top().key);
765 }
766 
TEST(IntrusiveHeapTest,InsertAscending)767 TEST(IntrusiveHeapTest, InsertAscending) {
768   IntrusiveHeap<TestElement> heap;
769 
770   for (int i = 0; i < 50; i++)
771     heap.insert({i, nullptr});
772 
773   EXPECT_EQ(0, heap.top().key);
774   EXPECT_EQ(50u, heap.size());
775 }
776 
TEST(IntrusiveHeapTest,InsertDescending)777 TEST(IntrusiveHeapTest, InsertDescending) {
778   IntrusiveHeap<TestElement> heap;
779 
780   for (int i = 0; i < 50; i++)
781     heap.insert({50 - i, nullptr});
782 
783   EXPECT_EQ(1, heap.top().key);
784   EXPECT_EQ(50u, heap.size());
785 }
786 
TEST(IntrusiveHeapTest,HeapIndex)787 TEST(IntrusiveHeapTest, HeapIndex) {
788   HeapHandle index5;
789   HeapHandle index4;
790   HeapHandle index3;
791   HeapHandle index2;
792   HeapHandle index1;
793   IntrusiveHeap<TestElement> heap;
794 
795   EXPECT_FALSE(index1.IsValid());
796   EXPECT_FALSE(index2.IsValid());
797   EXPECT_FALSE(index3.IsValid());
798   EXPECT_FALSE(index4.IsValid());
799   EXPECT_FALSE(index5.IsValid());
800 
801   heap.insert({15, &index5});
802   heap.insert({14, &index4});
803   heap.insert({13, &index3});
804   heap.insert({12, &index2});
805   heap.insert({11, &index1});
806 
807   EXPECT_TRUE(index1.IsValid());
808   EXPECT_TRUE(index2.IsValid());
809   EXPECT_TRUE(index3.IsValid());
810   EXPECT_TRUE(index4.IsValid());
811   EXPECT_TRUE(index5.IsValid());
812 
813   EXPECT_FALSE(heap.empty());
814 }
815 
TEST(IntrusiveHeapTest,HeapIndexDuplicates)816 TEST(IntrusiveHeapTest, HeapIndexDuplicates) {
817   HeapHandle index2;
818   HeapHandle index1;
819   IntrusiveHeap<TestElement> heap;
820 
821   EXPECT_FALSE(index1.IsValid());
822   EXPECT_FALSE(index2.IsValid());
823 
824   heap.insert({2, &index2});
825   heap.insert({2, &index1});
826 
827   EXPECT_TRUE(index1.IsValid());
828   EXPECT_TRUE(index2.IsValid());
829 
830   EXPECT_EQ(2U, heap.size());
831 }
832 
TEST(IntrusiveHeapTest,Pop)833 TEST(IntrusiveHeapTest, Pop) {
834   IntrusiveHeap<TestElement> heap;
835   HeapHandle index1;
836   HeapHandle index2;
837 
838   heap.insert({11, &index1});
839   heap.insert({12, &index2});
840   EXPECT_EQ(2u, heap.size());
841   EXPECT_TRUE(index1.IsValid());
842   EXPECT_TRUE(index2.IsValid());
843 
844   heap.pop();
845   EXPECT_EQ(1u, heap.size());
846   EXPECT_FALSE(index1.IsValid());
847   EXPECT_TRUE(index2.IsValid());
848 
849   heap.pop();
850   EXPECT_EQ(0u, heap.size());
851   EXPECT_FALSE(index1.IsValid());
852   EXPECT_FALSE(index2.IsValid());
853 }
854 
TEST(IntrusiveHeapTest,PopMany)855 TEST(IntrusiveHeapTest, PopMany) {
856   IntrusiveHeap<TestElement> heap;
857 
858   for (int i = 0; i < 500; i++)
859     heap.insert({i, nullptr});
860 
861   EXPECT_FALSE(heap.empty());
862   EXPECT_EQ(500u, heap.size());
863   for (int i = 0; i < 500; i++) {
864     EXPECT_EQ(i, heap.top().key);
865     heap.pop();
866   }
867   EXPECT_TRUE(heap.empty());
868 }
869 
TEST(IntrusiveHeapTest,Erase)870 TEST(IntrusiveHeapTest, Erase) {
871   IntrusiveHeap<TestElement> heap;
872 
873   HeapHandle index12;
874 
875   heap.insert({15, nullptr});
876   heap.insert({14, nullptr});
877   heap.insert({13, nullptr});
878   heap.insert({12, &index12});
879   heap.insert({11, nullptr});
880 
881   EXPECT_EQ(5u, heap.size());
882   EXPECT_TRUE(index12.IsValid());
883   heap.erase(index12);
884   EXPECT_EQ(4u, heap.size());
885   EXPECT_FALSE(index12.IsValid());
886 
887   EXPECT_EQ(11, heap.top().key);
888   heap.pop();
889   EXPECT_EQ(13, heap.top().key);
890   heap.pop();
891   EXPECT_EQ(14, heap.top().key);
892   heap.pop();
893   EXPECT_EQ(15, heap.top().key);
894   heap.pop();
895   EXPECT_TRUE(heap.empty());
896 }
897 
TEST(IntrusiveHeapTest,ReplaceTop)898 TEST(IntrusiveHeapTest, ReplaceTop) {
899   IntrusiveHeap<TestElement> heap;
900 
901   for (int i = 0; i < 500; i++)
902     heap.insert({500 - i, nullptr});
903 
904   EXPECT_EQ(1, heap.top().key);
905 
906   for (int i = 0; i < 500; i++)
907     heap.ReplaceTop({1000 + i, nullptr});
908 
909   EXPECT_EQ(1000, heap.top().key);
910 }
911 
TEST(IntrusiveHeapTest,ReplaceTopWithNonLeafNode)912 TEST(IntrusiveHeapTest, ReplaceTopWithNonLeafNode) {
913   IntrusiveHeap<TestElement> heap;
914 
915   for (int i = 0; i < 50; i++) {
916     heap.insert({i, nullptr});
917     heap.insert({200 + i, nullptr});
918   }
919 
920   EXPECT_EQ(0, heap.top().key);
921 
922   for (int i = 0; i < 50; i++)
923     heap.ReplaceTop({100 + i, nullptr});
924 
925   for (int i = 0; i < 50; i++) {
926     EXPECT_EQ((100 + i), heap.top().key);
927     heap.pop();
928   }
929   for (int i = 0; i < 50; i++) {
930     EXPECT_EQ((200 + i), heap.top().key);
931     heap.pop();
932   }
933   EXPECT_TRUE(heap.empty());
934 }
935 
TEST(IntrusiveHeapTest,ReplaceTopCheckAllFinalPositions)936 TEST(IntrusiveHeapTest, ReplaceTopCheckAllFinalPositions) {
937   HeapHandle index[100];
938   HeapHandle top_index;
939 
940   for (int j = -1; j <= 201; j += 2) {
941     IntrusiveHeap<TestElement> heap;
942     for (size_t i = 0; i < 100; i++) {
943       heap.insert({static_cast<int>(i) * 2, &index[i]});
944     }
945 
946     heap.ReplaceTop({j, &top_index});
947 
948     int prev = -2;
949     while (!heap.empty()) {
950       DCHECK_GT(heap.top().key, prev);
951       DCHECK(heap.top().key == j || (heap.top().key % 2) == 0);
952       DCHECK_NE(heap.top().key, 0);
953       prev = heap.top().key;
954       heap.pop();
955     }
956   }
957 }
958 
TEST(IntrusiveHeapTest,ReplaceUp)959 TEST(IntrusiveHeapTest, ReplaceUp) {
960   IntrusiveHeap<TestElement> heap;
961   HeapHandle index[10];
962 
963   for (size_t i = 0; i < 10; i++) {
964     heap.insert({static_cast<int>(i) * 2, &index[i]});
965   }
966 
967   heap.Replace(index[5], {17, &index[5]});
968 
969   std::vector<int> results;
970   while (!heap.empty()) {
971     results.push_back(heap.top().key);
972     heap.pop();
973   }
974 
975   EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 12, 14, 16, 17, 18));
976 }
977 
TEST(IntrusiveHeapTest,ReplaceUpButDoesntMove)978 TEST(IntrusiveHeapTest, ReplaceUpButDoesntMove) {
979   IntrusiveHeap<TestElement> heap;
980   HeapHandle index[10];
981 
982   for (size_t i = 0; i < 10; i++) {
983     heap.insert({static_cast<int>(i) * 2, &index[i]});
984   }
985 
986   heap.Replace(index[5], {11, &index[5]});
987 
988   std::vector<int> results;
989   while (!heap.empty()) {
990     results.push_back(heap.top().key);
991     heap.pop();
992   }
993 
994   EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 11, 12, 14, 16, 18));
995 }
996 
TEST(IntrusiveHeapTest,ReplaceDown)997 TEST(IntrusiveHeapTest, ReplaceDown) {
998   IntrusiveHeap<TestElement> heap;
999   HeapHandle index[10];
1000 
1001   for (size_t i = 0; i < 10; i++) {
1002     heap.insert({static_cast<int>(i) * 2, &index[i]});
1003   }
1004 
1005   heap.Replace(index[5], {1, &index[5]});
1006 
1007   std::vector<int> results;
1008   while (!heap.empty()) {
1009     results.push_back(heap.top().key);
1010     heap.pop();
1011   }
1012 
1013   EXPECT_THAT(results, testing::ElementsAre(0, 1, 2, 4, 6, 8, 12, 14, 16, 18));
1014 }
1015 
TEST(IntrusiveHeapTest,ReplaceDownButDoesntMove)1016 TEST(IntrusiveHeapTest, ReplaceDownButDoesntMove) {
1017   IntrusiveHeap<TestElement> heap;
1018   HeapHandle index[10];
1019 
1020   for (size_t i = 0; i < 10; i++) {
1021     heap.insert({static_cast<int>(i) * 2, &index[i]});
1022   }
1023 
1024   heap.Replace(index[5], {9, &index[5]});
1025 
1026   std::vector<int> results;
1027   while (!heap.empty()) {
1028     results.push_back(heap.top().key);
1029     heap.pop();
1030   }
1031 
1032   EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 9, 12, 14, 16, 18));
1033 }
1034 
TEST(IntrusiveHeapTest,ReplaceCheckAllFinalPositions)1035 TEST(IntrusiveHeapTest, ReplaceCheckAllFinalPositions) {
1036   HeapHandle index[100];
1037 
1038   for (int j = -1; j <= 201; j += 2) {
1039     IntrusiveHeap<TestElement> heap;
1040     for (size_t i = 0; i < 100; i++) {
1041       heap.insert({static_cast<int>(i) * 2, &index[i]});
1042     }
1043 
1044     heap.Replace(index[40], {j, &index[40]});
1045 
1046     int prev = -2;
1047     while (!heap.empty()) {
1048       DCHECK_GT(heap.top().key, prev);
1049       DCHECK(heap.top().key == j || (heap.top().key % 2) == 0);
1050       DCHECK_NE(heap.top().key, 80);
1051       prev = heap.top().key;
1052       heap.pop();
1053     }
1054   }
1055 }
1056 
TEST(IntrusiveHeapTest,At)1057 TEST(IntrusiveHeapTest, At) {
1058   HeapHandle index[10];
1059   IntrusiveHeap<TestElement> heap;
1060 
1061   for (int i = 0; i < 10; i++)
1062     heap.insert({static_cast<int>(i ^ (i + 1)), &index[i]});
1063 
1064   for (int i = 0; i < 10; i++) {
1065     EXPECT_EQ(heap.at(index[i]).key, i ^ (i + 1));
1066     EXPECT_EQ(heap.at(index[i]).handle, &index[i]);
1067   }
1068 }
1069 
IsEven(int i)1070 bool IsEven(int i) {
1071   return i % 2 == 0;
1072 }
1073 
TEST(IntrusiveHeapTest,EraseIf)1074 TEST(IntrusiveHeapTest, EraseIf) {
1075   HeapHandle index[10];
1076   IntrusiveHeap<TestElement> heap;
1077 
1078   for (int i = 0; i < 10; i++)
1079     heap.insert({i, &index[i]});
1080   ASSERT_EQ(heap.size(), 10u);
1081 
1082   // Remove all even elements.
1083   heap.EraseIf([](const TestElement& element) { return IsEven(element.key); });
1084   ASSERT_EQ(heap.size(), 5u);
1085 
1086   // Handles were correctly updated.
1087   for (int i = 0; i < 10; i++)
1088     EXPECT_EQ(IsEven(i), !index[i].IsValid());
1089 
1090   // Now iterate over all elements of the heap and check their handles.
1091   for (size_t i = 0; i < heap.size(); i++) {
1092     auto it = heap.begin();
1093     std::advance(it, i);
1094 
1095     // Retrieve the value of the element at this position.
1096     int value = it->key;
1097 
1098     // Its handle should have the correct index.
1099     EXPECT_EQ(index[value].index(), i);
1100   }
1101 
1102   std::vector<int> results;
1103   while (!heap.empty()) {
1104     results.push_back(heap.top().key);
1105     heap.pop();
1106   }
1107 
1108   EXPECT_THAT(results, testing::ElementsAre(1, 3, 5, 7, 9));
1109 }
1110 
1111 // A comparator class whose sole purpose is to allow the insertion of a
1112 // ScopedClosureRunner inside the heap. The ordering does not matter.
1113 class Comparator {
1114  public:
operator ()(const WithHeapHandle<ScopedClosureRunner> & lhs,const WithHeapHandle<ScopedClosureRunner> & rhs)1115   bool operator()(const WithHeapHandle<ScopedClosureRunner>& lhs,
1116                   const WithHeapHandle<ScopedClosureRunner>& rhs) {
1117     // Treat all closures as equal.
1118     return true;
1119   }
1120 };
1121 
1122 // Tests that inserting another element from the destructor of an object removed
1123 // during EraseIf() doesn't crash.
TEST(IntrusiveHeapTest,EraseIf_Reentrancy)1124 TEST(IntrusiveHeapTest, EraseIf_Reentrancy) {
1125   IntrusiveHeap<WithHeapHandle<ScopedClosureRunner>, Comparator> heap;
1126 
1127   // The task that will post a new element inside the heap upon destruction of
1128   // the first.
1129   OnceClosure insert_task = BindLambdaForTesting([&]() {
1130     // Insert a null callback so it can be differentiated.
1131     heap.insert(OnceClosure());
1132   });
1133   heap.insert(ScopedClosureRunner(std::move(insert_task)));
1134 
1135   // The heap contains the non-null closure.
1136   EXPECT_EQ(heap.size(), 1u);
1137   EXPECT_TRUE(heap.top().value());
1138 
1139   // Erase the only element using EraseIf().
1140   heap.EraseIf([](const auto& element) { return true; });
1141 
1142   // Now the heap contains the null closure.
1143   EXPECT_EQ(heap.size(), 1u);
1144   EXPECT_FALSE(heap.top().value());
1145 }
1146 
1147 }  // namespace base
1148