]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
test/osd: test new functionality added to the not-before queue
authorRonen Friedman <rfriedma@redhat.com>
Sun, 18 Aug 2024 17:33:38 +0000 (12:33 -0500)
committerRonen Friedman <rfriedma@redhat.com>
Sun, 25 Aug 2024 13:01:00 +0000 (08:01 -0500)
Signed-off-by: Ronen Friedman <rfriedma@redhat.com>
src/test/test_not_before_queue.cc

index 57241ecd91c534db0ef54dd125db6cf6610311f2..2dbb67f4813d2dbd86184105cce104d17a063172 100644 (file)
@@ -2,6 +2,7 @@
 // vim: ts=8 sw=2 smarttab
 
 #include <iostream>
+#include <string_view>
 
 #include "common/not_before_queue.h"
 #include "gtest/gtest.h"
@@ -23,14 +24,18 @@ struct tv_t {
   unsigned not_before = 0;
   unsigned ordering_value = 0;
   unsigned removal_class = 0;
+  // some additional "playing material" for tests to test/collect:
+  std::string some_data{"-"};
 
   tv_t() = default;
   tv_t(const tv_t &) = default;
   tv_t(unsigned not_before, unsigned ov, unsigned rc)
     : not_before(not_before), ordering_value(ov), removal_class(rc) {}
-
+  tv_t(unsigned not_before, unsigned ov, unsigned rc, std::string_view sd)
+      : not_before(not_before), ordering_value(ov), removal_class(rc)
+      , some_data{sd} {}
   auto to_tuple() const {
-    return std::make_tuple(not_before, ordering_value, removal_class);
+    return std::make_tuple(not_before, ordering_value, removal_class, some_data);
   }
   bool operator==(const tv_t &rhs) const {
     return to_tuple() == rhs.to_tuple();
@@ -54,8 +59,17 @@ bool operator<(const tv_t &lhs, const tv_t &rhs) {
 }
 
 class NotBeforeTest : public testing::Test {
-protected:
-  not_before_queue_t<tv_t, test_time_t> queue;
+ public:
+  using queue_t = not_before_queue_t<tv_t, test_time_t>;
+
+  void load_test_data(const std::vector<tv_t> &dt) {
+    for (const auto &d : dt) {
+      queue.enqueue(d);
+    }
+  }
+
+ protected:
+  queue_t queue;
 
   void dump() {
     std::cout << "Dumping queue: " << std::endl;
@@ -133,5 +147,216 @@ TEST_F(NotBeforeTest, RemoveByClass) {
   ASSERT_EQ(queue.dequeue(), std::nullopt);
 }
 
+TEST_F(NotBeforeTest, DequeueByPred) {
+  // the predicate we'll use is against the removal class
+  const auto pred = [](const tv_t &v) { return 0 == (v.removal_class % 2); };
+
+  tv_t e0t{1, 0, 10};
+  tv_t e1t{2, 2, 20};
+  tv_t e2t{1, 1, 12};
+  tv_t e3t{2, 1, 40};
+  tv_t e0f{1, 1, 17};
+  tv_t e1f{2, 2, 27};
+  tv_t e2f{1, 0, 17};
+  tv_t e3f{2, 1, 47};
+
+  queue.enqueue(e0f);
+  queue.enqueue(e1f);
+  queue.enqueue(e2f);
+  queue.enqueue(e3f);
+  queue.enqueue(e0t);
+  queue.enqueue(e1t);
+  queue.enqueue(e2t);
+  queue.enqueue(e3t);
+
+  // no ready entries
+  ASSERT_EQ(queue.dequeue(), std::nullopt);
+  ASSERT_EQ(queue.dequeue_by_pred(pred), std::nullopt);
+
+  // advance time to make e0* and e2* ready
+  queue.advance_time(1);
+  ASSERT_EQ(queue.dequeue_by_pred(pred), std::make_optional(e0t));
+  ASSERT_EQ(queue.dequeue_by_pred(pred), std::make_optional(e2t));
+  ASSERT_EQ(queue.dequeue_by_pred(pred), std::nullopt);
+
+  // advance time to make e1* and e3* ready
+  queue.advance_time(4);
+  ASSERT_EQ(queue.dequeue_by_pred(pred), std::make_optional(e3t));
+  ASSERT_EQ(queue.dequeue_by_pred(pred), std::make_optional(e1t));
+  ASSERT_EQ(queue.dequeue_by_pred(pred), std::nullopt);
+
+  // without the condition?
+  ASSERT_EQ(queue.dequeue(), std::make_optional(e2f));
+}
+
+namespace {
+  // clang-format off
+  const std::vector<tv_t> by_class_test_data_1{
+    {0, 20, 17}, {2, 10, 17},
+    {0, 20, 23}, {2, 10, 23},
+    {0, 10, 17}, {2, 20, 17},
+    {0, 10, 23}, {2, 10, 23},
+    {7, 41, 57}, {2, 61, 57},
+    {7, 42, 53}, {2, 62, 53},
+    {7, 43, 57}, {2, 63, 57},
+    {7, 41, 57}, {4, 41, 57},
+    {7, 42, 53}, {4, 42, 53},
+    {7, 43, 57}, {4, 43, 57},
+    {7, 44, 53}, {2, 44, 53}
+  };
+  // clang-format on
+}  // namespace
+
+TEST_F(NotBeforeTest, RemoveIfByClass_no_cond) {
+  load_test_data(by_class_test_data_1);
+  queue.advance_time(1);
+  ASSERT_EQ(queue.total_count(), 22);
+  ASSERT_EQ(queue.eligible_count(), 4);
+
+  // removing less than / more than available matches
+  EXPECT_EQ(
+      queue.remove_if_by_class(
+         17, [](const tv_t &v) { return true; }, 1),
+      1);
+  EXPECT_EQ(
+      queue.remove_if_by_class(
+         17, [](const tv_t &v) { return true; }, 10),
+      3);
+  EXPECT_EQ(
+      queue.remove_if_by_class(
+         57, [](const tv_t &v) { return v.ordering_value == 41; }),
+      3);
+}
+
+TEST_F(NotBeforeTest, RemoveIfByClass_with_cond) {
+  load_test_data(by_class_test_data_1);
+  queue.advance_time(2);
+  queue.advance_time(2);  // again, as theoretically that may happen
+  ASSERT_EQ(queue.eligible_count(), 12);
 
+  // rm from both eligible and non-eligible
+  EXPECT_EQ(
+      queue.remove_if_by_class(
+         57, [](const tv_t &v) { return v.ordering_value == 43; }),
+      3);
+  EXPECT_EQ(
+      queue.remove_if_by_class(
+         53, [](const tv_t &v) { return v.ordering_value == 44; }),
+      2);
+
+  ASSERT_EQ(queue.total_count(), 17);
+  EXPECT_EQ(
+      queue.remove_if_by_class(
+         57, [](const tv_t &v) { return v.ordering_value > 10; }, 20),
+      5);
+}
+
+TEST_F(NotBeforeTest, accumulate_1) {
+  // clang-format off
+  const std::vector<tv_t> accum_test_data {
+    /*1*/ {11, 105, 1010, "o5d"},
+    /*2*/ {10, 101, 1010, "j1c"},
+    /*3*/ { 2, 108, 1010, "r8b"},
+    /*4*/ { 1, 104, 1010, "p4a"},
+    /*5*/ {40, 103, 1010, "m3g"},
+    /*6*/ {41, 102, 1010, "u2h"},
+    /*7*/ {30, 106, 1010, "v6e"},
+    /*8*/ {31, 107, 1010, "e7f"}
+  };
+  // clang-format on
+  const auto acc_just_elig_templ = [](std::string &&acc, const tv_t &v,
+                                     bool is_eligible) {
+    if (is_eligible) {
+      acc += v.some_data[0];
+    }
+    return std::move(acc);
+  };
+
+  load_test_data(accum_test_data);
+
+  // set time to 2: only 3 and 4 are eligible, and will
+  // be collected first.
+  queue.advance_time(2);
+  auto acc_just_elig = acc_just_elig_templ;
+  auto res = queue.accumulate<std::string, decltype(acc_just_elig)>(
+      std::move(acc_just_elig));
+  EXPECT_EQ(res, "pr");
+
+  // an accumulator that has a non-empty closure:
+  int acc_index = 1;
+  auto acc_all = [&acc_index](std::string &&acc, const tv_t &v, bool) {
+    acc += v.some_data[acc_index];
+    return std::move(acc);
+  };
+  acc_index = 2;
+  auto res_all =
+      queue.accumulate<std::string, decltype(acc_all)>(std::move(acc_all));
+  EXPECT_EQ(res_all, "abcdefgh");
+
+  // set time to 20: the order changes: 2, 4, 1, 3
+  queue.advance_time(20);
+  acc_just_elig = acc_just_elig_templ;
+  res = queue.accumulate<std::string, decltype(acc_just_elig)>(
+      std::move(acc_just_elig));
+  EXPECT_EQ(res, "jpor");
+
+  // at 35: 2, 4, 1, 7, 8, 3
+  queue.advance_time(35);
+  acc_just_elig = acc_just_elig_templ;
+  res = queue.accumulate<std::string, decltype(acc_just_elig)>(
+      std::move(acc_just_elig));
+  EXPECT_EQ(res, "jpover");
+
+  // all jobs are eligible at 50
+  queue.advance_time(50);
+  acc_just_elig = acc_just_elig_templ;
+  res = queue.accumulate<std::string, decltype(acc_just_elig)>(
+      std::move(acc_just_elig));
+  EXPECT_EQ(res, "jumpover");
+}
+
+namespace {
+// clang-format off
+const std::vector<tv_t> for_each_test_data{
+  {11, 105, 1010, "before"},
+  {10, 101, 1010, "before"},
+  { 2, 108, 1010, "before"},
+  { 1, 104, 4010, "before"},
+  {40, 103, 4010, "before"},
+  {41, 102, 6010, "before"},
+  {30, 106, 4010, "before"},
+  {31, 107, 6010, "before"}
+};
+// clang-format on
+}  // namespace
+
+TEST_F(NotBeforeTest, forEachN_low_max) {
+  load_test_data(for_each_test_data);
+  int jobs_cnt[2] = {0, 0};
+  const auto f_template = [&](const tv_t &v, bool is_eligible) {
+    jobs_cnt[is_eligible ? 1 : 0]++;
+  };
+
+  // with a low max-count
+  queue.advance_time(20);
+  auto f1 = f_template;
+  queue.for_each_n<decltype(f1)>(std::move(f1), 5);
+  EXPECT_EQ(jobs_cnt[0], 1);
+  EXPECT_EQ(jobs_cnt[1], 4);
+}
+
+TEST_F(NotBeforeTest, forEachN_high_max) {
+  load_test_data(for_each_test_data);
+  int jobs_cnt[2] = {0, 0};
+  const auto f_template = [&](const tv_t &v, bool is_eligible) {
+    jobs_cnt[is_eligible ? 1 : 0]++;
+  };
+
+  // max-count > total count
+  queue.advance_time(20);
+  auto f1 = f_template;
+  queue.for_each_n<decltype(f1)>(std::move(f1), 20);
+  EXPECT_EQ(jobs_cnt[0], 4);
+  EXPECT_EQ(jobs_cnt[1], 4);
+}