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