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