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_exclusion.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<T>::value &&
147 std::is_copy_constructible<T>::value;
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::__anon449594180111::Take155 static void Do(IntrusiveHeap<T>* heap, size_t index) { heap->erase(index); }
156 };
157 template <typename T>
158 struct Take<T, false> {
Dobase::__anon449594180111::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::__anon449594180111::TakeTop172 static void Do(IntrusiveHeap<T>* heap) { heap->pop(); }
173 };
174 template <typename T>
175 struct TakeTop<T, false> {
Dobase::__anon449594180111::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<ValueType>::value == D, "oops");
520 static_assert(std::is_move_constructible<ValueType>::value == M, "oops");
521 static_assert(std::is_move_assignable<ValueType>::value == M, "oops");
522 static_assert(std::is_copy_constructible<ValueType>::value == C, "oops");
523 static_assert(std::is_copy_assignable<ValueType>::value == 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 // This field is not a raw_ptr<> because it was filtered by the rewriter for:
531 // #reinterpret-cast-trivial-type
532 RAW_PTR_EXCLUSION HeapHandle* handle;
533
534 // Make this a min-heap by return > instead of <.
operator <base::__anon449594180111::TestElement535 bool operator<(const TestElement& other) const { return key > other.key; }
536
SetHeapHandlebase::__anon449594180111::TestElement537 void SetHeapHandle(HeapHandle h) {
538 if (handle)
539 *handle = h;
540 }
541
ClearHeapHandlebase::__anon449594180111::TestElement542 void ClearHeapHandle() {
543 if (handle)
544 handle->reset();
545 }
546
GetHeapHandlebase::__anon449594180111::TestElement547 HeapHandle GetHeapHandle() const {
548 if (handle)
549 return *handle;
550 return HeapHandle::Invalid();
551 }
552 };
553
554 } // namespace
555
556 ////////////////////////////////////////////////////////////////////////////////
557 // TEST SUITE 1
558 //
559 // Explicit tests of a simple heap using WithHeapHandle<>.
560
TEST(IntrusiveHeapTest,Constructors)561 TEST(IntrusiveHeapTest, Constructors) {
562 {
563 // Default constructor.
564 IntrusiveHeapInt heap;
565 EXPECT_TRUE(heap.empty());
566 }
567
568 {
569 // Constructor with iterators.
570 std::vector<int> ints{CANONICAL_ELEMENTS};
571 IntrusiveHeapInt heap(ints.begin(), ints.end());
572 ExpectCanonical(heap);
573
574 // Move constructor.
575 IntrusiveHeapInt heap2(std::move(heap));
576 EXPECT_TRUE(heap.empty());
577 ExpectCanonical(heap2);
578 }
579
580 {
581 // Constructor with initializer list.
582 IntrusiveHeapInt heap{CANONICAL_ELEMENTS};
583 ExpectCanonical(heap);
584 }
585 }
586
TEST(IntrusiveHeapTest,Assignment)587 TEST(IntrusiveHeapTest, Assignment) {
588 IntrusiveHeapInt heap{CANONICAL_ELEMENTS};
589
590 // Move assignment.
591 IntrusiveHeapInt heap2;
592 heap2 = std::move(heap);
593 EXPECT_TRUE(heap.empty());
594 ExpectCanonical(heap2);
595 }
596
TEST(IntrusiveHeapTest,Swap)597 TEST(IntrusiveHeapTest, Swap) {
598 IntrusiveHeapInt heap{CANONICAL_ELEMENTS};
599 IntrusiveHeapInt heap2;
600 swap(heap, heap2);
601 EXPECT_TRUE(heap.empty());
602 ExpectCanonical(heap2);
603 heap.swap(heap2);
604 EXPECT_TRUE(heap2.empty());
605 ExpectCanonical(heap);
606 }
607
TEST(IntrusiveHeapTest,ElementAccess)608 TEST(IntrusiveHeapTest, ElementAccess) {
609 IntrusiveHeapInt heap{CANONICAL_ELEMENTS};
610 EXPECT_EQ(heap.front(), heap[0]);
611 EXPECT_EQ(heap.back(), heap[7]);
612 EXPECT_EQ(heap.top(), heap[0]);
613 for (size_t i = 0; i < heap.size(); ++i) {
614 EXPECT_EQ(heap[i], heap.at(i));
615 EXPECT_EQ(heap[i], heap.data()[i]);
616 }
617 }
618
TEST(IntrusiveHeapTest,SizeManagement)619 TEST(IntrusiveHeapTest, SizeManagement) {
620 IntrusiveHeapInt heap;
621 EXPECT_TRUE(heap.empty());
622 EXPECT_LE(heap.size(), heap.capacity());
623
624 MakeCanonical(&heap);
625 EXPECT_FALSE(heap.empty());
626 EXPECT_LE(heap.size(), heap.capacity());
627 }
628
TEST(IntrusiveHeapTest,Iterators)629 TEST(IntrusiveHeapTest, Iterators) {
630 IntrusiveHeapInt heap;
631 MakeCanonical(&heap);
632
633 size_t i = 0;
634 for (auto it = heap.begin(); it != heap.end(); ++it) {
635 EXPECT_EQ(i, heap.ToIndex(it));
636 EXPECT_EQ(&(*it), heap.data() + i);
637 ++i;
638 }
639
640 i = heap.size() - 1;
641 for (auto rit = heap.rbegin(); rit != heap.rend(); ++rit) {
642 EXPECT_EQ(i, heap.ToIndex(rit));
643 EXPECT_EQ(&(*rit), heap.data() + i);
644 --i;
645 }
646 }
647
648 ////////////////////////////////////////////////////////////////////////////////
649 // TEST SUITE 2
650 //
651 // Exhaustive stress tests with different value types, exploring all
652 // possibilities of default-constrible, movable and copyable value types.
653
TEST(IntrusiveHeapTest,MoveOnlyNoDefaultConstructorTest)654 TEST(IntrusiveHeapTest, MoveOnlyNoDefaultConstructorTest) {
655 using ValueType = Value_dMc;
656 ValidateValueType<ValueType, false, true, false>();
657 MoveStressTest<ValueType>();
658 GeneralStressTest<ValueType>();
659 }
660
TEST(IntrusiveHeapTest,CopyOnlyNoDefaultConstructorTest)661 TEST(IntrusiveHeapTest, CopyOnlyNoDefaultConstructorTest) {
662 using ValueType = Value_dmC;
663 ValidateValueType<ValueType, false, false, true>();
664 // We cannot perform CopyStressTest nor GeneralStressTest here, because
665 // Value_dmC has deleted move constructor and assignment operator. See
666 // crbug.com/1022576.
667 }
668
TEST(IntrusiveHeapTest,CopyAndMoveNoDefaultConstructorTest)669 TEST(IntrusiveHeapTest, CopyAndMoveNoDefaultConstructorTest) {
670 using ValueType = Value_dMC;
671 ValidateValueType<ValueType, false, true, true>();
672 CopyStressTest<ValueType>();
673 MoveStressTest<ValueType>();
674 GeneralStressTest<ValueType>();
675 }
676
TEST(IntrusiveHeapTest,MoveOnlyWithDefaultConstructorTest)677 TEST(IntrusiveHeapTest, MoveOnlyWithDefaultConstructorTest) {
678 using ValueType = Value_DMc;
679 ValidateValueType<ValueType, true, true, false>();
680 MoveStressTest<ValueType>();
681 GeneralStressTest<ValueType>();
682 }
683
TEST(IntrusiveHeapTest,CopyOnlyWithDefaultConstructorTest)684 TEST(IntrusiveHeapTest, CopyOnlyWithDefaultConstructorTest) {
685 using ValueType = Value_DmC;
686 ValidateValueType<ValueType, true, false, true>();
687 // We cannot perform CopyStressTest nor GeneralStressTest here, because
688 // Value_DmC has deleted move constructor and assignment operator. See
689 // crbug.com/1022576.
690 }
691
TEST(IntrusiveHeapTest,CopyAndMoveWithDefaultConstructorTest)692 TEST(IntrusiveHeapTest, CopyAndMoveWithDefaultConstructorTest) {
693 using ValueType = Value_DMC;
694 ValidateValueType<ValueType, true, true, true>();
695 CopyStressTest<ValueType>();
696 MoveStressTest<ValueType>();
697 GeneralStressTest<ValueType>();
698 }
699
700 ////////////////////////////////////////////////////////////////////////////////
701 // TEST SUITE 3
702 //
703 // Tests individual functions on a custom type that provides heap handle storage
704 // externally through raw pointers.
705
TEST(IntrusiveHeapTest,Basic)706 TEST(IntrusiveHeapTest, Basic) {
707 IntrusiveHeap<TestElement> heap;
708
709 EXPECT_TRUE(heap.empty());
710 EXPECT_EQ(0u, heap.size());
711 }
712
TEST(IntrusiveHeapTest,Clear)713 TEST(IntrusiveHeapTest, Clear) {
714 IntrusiveHeap<TestElement> heap;
715 HeapHandle index1;
716
717 heap.insert({11, &index1});
718 EXPECT_EQ(1u, heap.size());
719 EXPECT_TRUE(index1.IsValid());
720
721 heap.clear();
722 EXPECT_EQ(0u, heap.size());
723 EXPECT_FALSE(index1.IsValid());
724 }
725
TEST(IntrusiveHeapTest,Destructor)726 TEST(IntrusiveHeapTest, Destructor) {
727 HeapHandle index1;
728
729 {
730 IntrusiveHeap<TestElement> heap;
731
732 heap.insert({11, &index1});
733 EXPECT_EQ(1u, heap.size());
734 EXPECT_TRUE(index1.IsValid());
735 }
736
737 EXPECT_FALSE(index1.IsValid());
738 }
739
TEST(IntrusiveHeapTest,Min)740 TEST(IntrusiveHeapTest, Min) {
741 IntrusiveHeap<TestElement> heap;
742
743 heap.insert({9, nullptr});
744 heap.insert({10, nullptr});
745 heap.insert({8, nullptr});
746 heap.insert({2, nullptr});
747 heap.insert({7, nullptr});
748 heap.insert({15, nullptr});
749 heap.insert({22, nullptr});
750 heap.insert({3, nullptr});
751
752 EXPECT_FALSE(heap.empty());
753 EXPECT_EQ(8u, heap.size());
754 EXPECT_EQ(2, heap.top().key);
755 }
756
TEST(IntrusiveHeapTest,MinDuplicates)757 TEST(IntrusiveHeapTest, MinDuplicates) {
758 IntrusiveHeap<TestElement> heap;
759
760 heap.insert({2, nullptr});
761 heap.insert({2, nullptr});
762 heap.insert({3, nullptr});
763
764 EXPECT_FALSE(heap.empty());
765 EXPECT_EQ(3u, heap.size());
766 EXPECT_EQ(2, heap.top().key);
767 }
768
TEST(IntrusiveHeapTest,InsertAscending)769 TEST(IntrusiveHeapTest, InsertAscending) {
770 IntrusiveHeap<TestElement> heap;
771
772 for (int i = 0; i < 50; i++)
773 heap.insert({i, nullptr});
774
775 EXPECT_EQ(0, heap.top().key);
776 EXPECT_EQ(50u, heap.size());
777 }
778
TEST(IntrusiveHeapTest,InsertDescending)779 TEST(IntrusiveHeapTest, InsertDescending) {
780 IntrusiveHeap<TestElement> heap;
781
782 for (int i = 0; i < 50; i++)
783 heap.insert({50 - i, nullptr});
784
785 EXPECT_EQ(1, heap.top().key);
786 EXPECT_EQ(50u, heap.size());
787 }
788
TEST(IntrusiveHeapTest,HeapIndex)789 TEST(IntrusiveHeapTest, HeapIndex) {
790 HeapHandle index5;
791 HeapHandle index4;
792 HeapHandle index3;
793 HeapHandle index2;
794 HeapHandle index1;
795 IntrusiveHeap<TestElement> heap;
796
797 EXPECT_FALSE(index1.IsValid());
798 EXPECT_FALSE(index2.IsValid());
799 EXPECT_FALSE(index3.IsValid());
800 EXPECT_FALSE(index4.IsValid());
801 EXPECT_FALSE(index5.IsValid());
802
803 heap.insert({15, &index5});
804 heap.insert({14, &index4});
805 heap.insert({13, &index3});
806 heap.insert({12, &index2});
807 heap.insert({11, &index1});
808
809 EXPECT_TRUE(index1.IsValid());
810 EXPECT_TRUE(index2.IsValid());
811 EXPECT_TRUE(index3.IsValid());
812 EXPECT_TRUE(index4.IsValid());
813 EXPECT_TRUE(index5.IsValid());
814
815 EXPECT_FALSE(heap.empty());
816 }
817
TEST(IntrusiveHeapTest,HeapIndexDuplicates)818 TEST(IntrusiveHeapTest, HeapIndexDuplicates) {
819 HeapHandle index2;
820 HeapHandle index1;
821 IntrusiveHeap<TestElement> heap;
822
823 EXPECT_FALSE(index1.IsValid());
824 EXPECT_FALSE(index2.IsValid());
825
826 heap.insert({2, &index2});
827 heap.insert({2, &index1});
828
829 EXPECT_TRUE(index1.IsValid());
830 EXPECT_TRUE(index2.IsValid());
831
832 EXPECT_EQ(2U, heap.size());
833 }
834
TEST(IntrusiveHeapTest,Pop)835 TEST(IntrusiveHeapTest, Pop) {
836 IntrusiveHeap<TestElement> heap;
837 HeapHandle index1;
838 HeapHandle index2;
839
840 heap.insert({11, &index1});
841 heap.insert({12, &index2});
842 EXPECT_EQ(2u, heap.size());
843 EXPECT_TRUE(index1.IsValid());
844 EXPECT_TRUE(index2.IsValid());
845
846 heap.pop();
847 EXPECT_EQ(1u, heap.size());
848 EXPECT_FALSE(index1.IsValid());
849 EXPECT_TRUE(index2.IsValid());
850
851 heap.pop();
852 EXPECT_EQ(0u, heap.size());
853 EXPECT_FALSE(index1.IsValid());
854 EXPECT_FALSE(index2.IsValid());
855 }
856
TEST(IntrusiveHeapTest,PopMany)857 TEST(IntrusiveHeapTest, PopMany) {
858 IntrusiveHeap<TestElement> heap;
859
860 for (int i = 0; i < 500; i++)
861 heap.insert({i, nullptr});
862
863 EXPECT_FALSE(heap.empty());
864 EXPECT_EQ(500u, heap.size());
865 for (int i = 0; i < 500; i++) {
866 EXPECT_EQ(i, heap.top().key);
867 heap.pop();
868 }
869 EXPECT_TRUE(heap.empty());
870 }
871
TEST(IntrusiveHeapTest,Erase)872 TEST(IntrusiveHeapTest, Erase) {
873 IntrusiveHeap<TestElement> heap;
874
875 HeapHandle index12;
876
877 heap.insert({15, nullptr});
878 heap.insert({14, nullptr});
879 heap.insert({13, nullptr});
880 heap.insert({12, &index12});
881 heap.insert({11, nullptr});
882
883 EXPECT_EQ(5u, heap.size());
884 EXPECT_TRUE(index12.IsValid());
885 heap.erase(index12);
886 EXPECT_EQ(4u, heap.size());
887 EXPECT_FALSE(index12.IsValid());
888
889 EXPECT_EQ(11, heap.top().key);
890 heap.pop();
891 EXPECT_EQ(13, heap.top().key);
892 heap.pop();
893 EXPECT_EQ(14, heap.top().key);
894 heap.pop();
895 EXPECT_EQ(15, heap.top().key);
896 heap.pop();
897 EXPECT_TRUE(heap.empty());
898 }
899
TEST(IntrusiveHeapTest,ReplaceTop)900 TEST(IntrusiveHeapTest, ReplaceTop) {
901 IntrusiveHeap<TestElement> heap;
902
903 for (int i = 0; i < 500; i++)
904 heap.insert({500 - i, nullptr});
905
906 EXPECT_EQ(1, heap.top().key);
907
908 for (int i = 0; i < 500; i++)
909 heap.ReplaceTop({1000 + i, nullptr});
910
911 EXPECT_EQ(1000, heap.top().key);
912 }
913
TEST(IntrusiveHeapTest,ReplaceTopWithNonLeafNode)914 TEST(IntrusiveHeapTest, ReplaceTopWithNonLeafNode) {
915 IntrusiveHeap<TestElement> heap;
916
917 for (int i = 0; i < 50; i++) {
918 heap.insert({i, nullptr});
919 heap.insert({200 + i, nullptr});
920 }
921
922 EXPECT_EQ(0, heap.top().key);
923
924 for (int i = 0; i < 50; i++)
925 heap.ReplaceTop({100 + i, nullptr});
926
927 for (int i = 0; i < 50; i++) {
928 EXPECT_EQ((100 + i), heap.top().key);
929 heap.pop();
930 }
931 for (int i = 0; i < 50; i++) {
932 EXPECT_EQ((200 + i), heap.top().key);
933 heap.pop();
934 }
935 EXPECT_TRUE(heap.empty());
936 }
937
TEST(IntrusiveHeapTest,ReplaceTopCheckAllFinalPositions)938 TEST(IntrusiveHeapTest, ReplaceTopCheckAllFinalPositions) {
939 HeapHandle index[100];
940 HeapHandle top_index;
941
942 for (int j = -1; j <= 201; j += 2) {
943 IntrusiveHeap<TestElement> heap;
944 for (size_t i = 0; i < 100; i++) {
945 heap.insert({static_cast<int>(i) * 2, &index[i]});
946 }
947
948 heap.ReplaceTop({j, &top_index});
949
950 int prev = -2;
951 while (!heap.empty()) {
952 DCHECK_GT(heap.top().key, prev);
953 DCHECK(heap.top().key == j || (heap.top().key % 2) == 0);
954 DCHECK_NE(heap.top().key, 0);
955 prev = heap.top().key;
956 heap.pop();
957 }
958 }
959 }
960
TEST(IntrusiveHeapTest,ReplaceUp)961 TEST(IntrusiveHeapTest, ReplaceUp) {
962 IntrusiveHeap<TestElement> heap;
963 HeapHandle index[10];
964
965 for (size_t i = 0; i < 10; i++) {
966 heap.insert({static_cast<int>(i) * 2, &index[i]});
967 }
968
969 heap.Replace(index[5], {17, &index[5]});
970
971 std::vector<int> results;
972 while (!heap.empty()) {
973 results.push_back(heap.top().key);
974 heap.pop();
975 }
976
977 EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 12, 14, 16, 17, 18));
978 }
979
TEST(IntrusiveHeapTest,ReplaceUpButDoesntMove)980 TEST(IntrusiveHeapTest, ReplaceUpButDoesntMove) {
981 IntrusiveHeap<TestElement> heap;
982 HeapHandle index[10];
983
984 for (size_t i = 0; i < 10; i++) {
985 heap.insert({static_cast<int>(i) * 2, &index[i]});
986 }
987
988 heap.Replace(index[5], {11, &index[5]});
989
990 std::vector<int> results;
991 while (!heap.empty()) {
992 results.push_back(heap.top().key);
993 heap.pop();
994 }
995
996 EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 11, 12, 14, 16, 18));
997 }
998
TEST(IntrusiveHeapTest,ReplaceDown)999 TEST(IntrusiveHeapTest, ReplaceDown) {
1000 IntrusiveHeap<TestElement> heap;
1001 HeapHandle index[10];
1002
1003 for (size_t i = 0; i < 10; i++) {
1004 heap.insert({static_cast<int>(i) * 2, &index[i]});
1005 }
1006
1007 heap.Replace(index[5], {1, &index[5]});
1008
1009 std::vector<int> results;
1010 while (!heap.empty()) {
1011 results.push_back(heap.top().key);
1012 heap.pop();
1013 }
1014
1015 EXPECT_THAT(results, testing::ElementsAre(0, 1, 2, 4, 6, 8, 12, 14, 16, 18));
1016 }
1017
TEST(IntrusiveHeapTest,ReplaceDownButDoesntMove)1018 TEST(IntrusiveHeapTest, ReplaceDownButDoesntMove) {
1019 IntrusiveHeap<TestElement> heap;
1020 HeapHandle index[10];
1021
1022 for (size_t i = 0; i < 10; i++) {
1023 heap.insert({static_cast<int>(i) * 2, &index[i]});
1024 }
1025
1026 heap.Replace(index[5], {9, &index[5]});
1027
1028 std::vector<int> results;
1029 while (!heap.empty()) {
1030 results.push_back(heap.top().key);
1031 heap.pop();
1032 }
1033
1034 EXPECT_THAT(results, testing::ElementsAre(0, 2, 4, 6, 8, 9, 12, 14, 16, 18));
1035 }
1036
TEST(IntrusiveHeapTest,ReplaceCheckAllFinalPositions)1037 TEST(IntrusiveHeapTest, ReplaceCheckAllFinalPositions) {
1038 HeapHandle index[100];
1039
1040 for (int j = -1; j <= 201; j += 2) {
1041 IntrusiveHeap<TestElement> heap;
1042 for (size_t i = 0; i < 100; i++) {
1043 heap.insert({static_cast<int>(i) * 2, &index[i]});
1044 }
1045
1046 heap.Replace(index[40], {j, &index[40]});
1047
1048 int prev = -2;
1049 while (!heap.empty()) {
1050 DCHECK_GT(heap.top().key, prev);
1051 DCHECK(heap.top().key == j || (heap.top().key % 2) == 0);
1052 DCHECK_NE(heap.top().key, 80);
1053 prev = heap.top().key;
1054 heap.pop();
1055 }
1056 }
1057 }
1058
TEST(IntrusiveHeapTest,At)1059 TEST(IntrusiveHeapTest, At) {
1060 HeapHandle index[10];
1061 IntrusiveHeap<TestElement> heap;
1062
1063 for (int i = 0; i < 10; i++)
1064 heap.insert({static_cast<int>(i ^ (i + 1)), &index[i]});
1065
1066 for (int i = 0; i < 10; i++) {
1067 EXPECT_EQ(heap.at(index[i]).key, i ^ (i + 1));
1068 EXPECT_EQ(heap.at(index[i]).handle, &index[i]);
1069 }
1070 }
1071
IsEven(int i)1072 bool IsEven(int i) {
1073 return i % 2 == 0;
1074 }
1075
TEST(IntrusiveHeapTest,EraseIf)1076 TEST(IntrusiveHeapTest, EraseIf) {
1077 HeapHandle index[10];
1078 IntrusiveHeap<TestElement> heap;
1079
1080 for (int i = 0; i < 10; i++)
1081 heap.insert({i, &index[i]});
1082 ASSERT_EQ(heap.size(), 10u);
1083
1084 // Remove all even elements.
1085 heap.EraseIf([](const TestElement& element) { return IsEven(element.key); });
1086 ASSERT_EQ(heap.size(), 5u);
1087
1088 // Handles were correctly updated.
1089 for (int i = 0; i < 10; i++)
1090 EXPECT_EQ(IsEven(i), !index[i].IsValid());
1091
1092 // Now iterate over all elements of the heap and check their handles.
1093 for (size_t i = 0; i < heap.size(); i++) {
1094 auto it = heap.begin();
1095 std::advance(it, i);
1096
1097 // Retrieve the value of the element at this position.
1098 int value = it->key;
1099
1100 // Its handle should have the correct index.
1101 EXPECT_EQ(index[value].index(), i);
1102 }
1103
1104 std::vector<int> results;
1105 while (!heap.empty()) {
1106 results.push_back(heap.top().key);
1107 heap.pop();
1108 }
1109
1110 EXPECT_THAT(results, testing::ElementsAre(1, 3, 5, 7, 9));
1111 }
1112
1113 // A comparator class whose sole purpose is to allow the insertion of a
1114 // ScopedClosureRunner inside the heap. The ordering does not matter.
1115 class Comparator {
1116 public:
operator ()(const WithHeapHandle<ScopedClosureRunner> & lhs,const WithHeapHandle<ScopedClosureRunner> & rhs)1117 bool operator()(const WithHeapHandle<ScopedClosureRunner>& lhs,
1118 const WithHeapHandle<ScopedClosureRunner>& rhs) {
1119 // Treat all closures as equal.
1120 return true;
1121 }
1122 };
1123
1124 // Tests that inserting another element from the destructor of an object removed
1125 // during EraseIf() doesn't crash.
TEST(IntrusiveHeapTest,EraseIf_Reentrancy)1126 TEST(IntrusiveHeapTest, EraseIf_Reentrancy) {
1127 IntrusiveHeap<WithHeapHandle<ScopedClosureRunner>, Comparator> heap;
1128
1129 // The task that will post a new element inside the heap upon destruction of
1130 // the first.
1131 OnceClosure insert_task = BindLambdaForTesting([&]() {
1132 // Insert a null callback so it can be differentiated.
1133 heap.insert(OnceClosure());
1134 });
1135 heap.insert(ScopedClosureRunner(std::move(insert_task)));
1136
1137 // The heap contains the non-null closure.
1138 EXPECT_EQ(heap.size(), 1u);
1139 EXPECT_TRUE(heap.top().value());
1140
1141 // Erase the only element using EraseIf().
1142 heap.EraseIf([](const auto& element) { return true; });
1143
1144 // Now the heap contains the null closure.
1145 EXPECT_EQ(heap.size(), 1u);
1146 EXPECT_FALSE(heap.top().value());
1147 }
1148
1149 } // namespace base
1150