heap_test.cc 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #include <gtest/gtest.h>
  6. #include <climits>
  7. #include <queue>
  8. #include <random>
  9. #include <utility>
  10. #include "util/heap.h"
  11. #ifndef GFLAGS
  12. const int64_t FLAGS_iters = 100000;
  13. #else
  14. #include "util/gflags_compat.h"
  15. DEFINE_int64(iters, 100000, "number of pseudo-random operations in each test");
  16. #endif // GFLAGS
  17. /*
  18. * Compares the custom heap implementation in util/heap.h against
  19. * std::priority_queue on a pseudo-random sequence of operations.
  20. */
  21. namespace ROCKSDB_NAMESPACE {
  22. using HeapTestValue = uint64_t;
  23. using Params = std::tuple<size_t, HeapTestValue, int64_t>;
  24. class HeapTest : public ::testing::TestWithParam<Params> {
  25. };
  26. TEST_P(HeapTest, Test) {
  27. // This test performs the same pseudorandom sequence of operations on a
  28. // BinaryHeap and an std::priority_queue, comparing output. The three
  29. // possible operations are insert, replace top and pop.
  30. //
  31. // Insert is chosen slightly more often than the others so that the size of
  32. // the heap slowly grows. Once the size heats the MAX_HEAP_SIZE limit, we
  33. // disallow inserting until the heap becomes empty, testing the "draining"
  34. // scenario.
  35. const auto MAX_HEAP_SIZE = std::get<0>(GetParam());
  36. const auto MAX_VALUE = std::get<1>(GetParam());
  37. const auto RNG_SEED = std::get<2>(GetParam());
  38. BinaryHeap<HeapTestValue> heap;
  39. std::priority_queue<HeapTestValue> ref;
  40. std::mt19937 rng(static_cast<unsigned int>(RNG_SEED));
  41. std::uniform_int_distribution<HeapTestValue> value_dist(0, MAX_VALUE);
  42. int ndrains = 0;
  43. bool draining = false; // hit max size, draining until we empty the heap
  44. size_t size = 0;
  45. for (int64_t i = 0; i < FLAGS_iters; ++i) {
  46. if (size == 0) {
  47. draining = false;
  48. }
  49. if (!draining &&
  50. (size == 0 || std::bernoulli_distribution(0.4)(rng))) {
  51. // insert
  52. HeapTestValue val = value_dist(rng);
  53. heap.push(val);
  54. ref.push(val);
  55. ++size;
  56. if (size == MAX_HEAP_SIZE) {
  57. draining = true;
  58. ++ndrains;
  59. }
  60. } else if (std::bernoulli_distribution(0.5)(rng)) {
  61. // replace top
  62. HeapTestValue val = value_dist(rng);
  63. heap.replace_top(val);
  64. ref.pop();
  65. ref.push(val);
  66. } else {
  67. // pop
  68. assert(size > 0);
  69. heap.pop();
  70. ref.pop();
  71. --size;
  72. }
  73. // After every operation, check that the public methods give the same
  74. // results
  75. assert((size == 0) == ref.empty());
  76. ASSERT_EQ(size == 0, heap.empty());
  77. if (size > 0) {
  78. ASSERT_EQ(ref.top(), heap.top());
  79. }
  80. }
  81. // Probabilities should be set up to occasionally hit the max heap size and
  82. // drain it
  83. assert(ndrains > 0);
  84. heap.clear();
  85. ASSERT_TRUE(heap.empty());
  86. }
  87. // Basic test, MAX_VALUE = 3*MAX_HEAP_SIZE (occasional duplicates)
  88. INSTANTIATE_TEST_CASE_P(
  89. Basic, HeapTest,
  90. ::testing::Values(Params(1000, 3000, 0x1b575cf05b708945))
  91. );
  92. // Mid-size heap with small values (many duplicates)
  93. INSTANTIATE_TEST_CASE_P(
  94. SmallValues, HeapTest,
  95. ::testing::Values(Params(100, 10, 0x5ae213f7bd5dccd0))
  96. );
  97. // Small heap, large value range (no duplicates)
  98. INSTANTIATE_TEST_CASE_P(
  99. SmallHeap, HeapTest,
  100. ::testing::Values(Params(10, ULLONG_MAX, 0x3e1fa8f4d01707cf))
  101. );
  102. // Two-element heap
  103. INSTANTIATE_TEST_CASE_P(
  104. TwoElementHeap, HeapTest,
  105. ::testing::Values(Params(2, 5, 0x4b5e13ea988c6abc))
  106. );
  107. // One-element heap
  108. INSTANTIATE_TEST_CASE_P(
  109. OneElementHeap, HeapTest,
  110. ::testing::Values(Params(1, 3, 0x176a1019ab0b612e))
  111. );
  112. } // namespace ROCKSDB_NAMESPACE
  113. int main(int argc, char** argv) {
  114. ::testing::InitGoogleTest(&argc, argv);
  115. #ifdef GFLAGS
  116. GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
  117. #endif // GFLAGS
  118. return RUN_ALL_TESTS();
  119. }