From: Ronen Friedman Date: Sun, 18 Aug 2024 17:33:38 +0000 (-0500) Subject: test/osd: test new functionality added to the not-before queue X-Git-Tag: v20.0.0~1219^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=26cd41c503b09eaaae44e81de265990ca266b4f7;p=ceph.git test/osd: test new functionality added to the not-before queue Signed-off-by: Ronen Friedman --- diff --git a/src/test/test_not_before_queue.cc b/src/test/test_not_before_queue.cc index 57241ecd91c..2dbb67f4813 100644 --- a/src/test/test_not_before_queue.cc +++ b/src/test/test_not_before_queue.cc @@ -2,6 +2,7 @@ // vim: ts=8 sw=2 smarttab #include +#include #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 queue; + public: + using queue_t = not_before_queue_t; + + void load_test_data(const std::vector &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 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 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::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::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::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::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::move(acc_just_elig)); + EXPECT_EQ(res, "jumpover"); +} + +namespace { +// clang-format off +const std::vector 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(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(std::move(f1), 20); + EXPECT_EQ(jobs_cnt[0], 4); + EXPECT_EQ(jobs_cnt[1], 4); +}