1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "gtest/gtest.h"
5 #include "common/Formatter.h"
6 #include "common/WeightedPriorityQueue.h"
14 #define CEPH_OP_CLASS_STRICT 0
15 #define CEPH_OP_CLASS_NORMAL 0
16 #define CEPH_OP_QUEUE_BACK 0
17 #define CEPH_OP_QUEUE_FRONT 0
19 class WeightedPriorityQueueTest
: public testing::Test
22 typedef unsigned Klass
;
23 // tuple<Prio, Klass, OpID> so that we can verfiy the op
24 typedef std::tuple
<unsigned, unsigned, unsigned> Item
;
25 typedef unsigned Prio
;
26 typedef unsigned Kost
;
27 typedef WeightedPriorityQueue
<Item
, Klass
> WQ
;
28 // Simulate queue structure
29 typedef std::list
<std::pair
<Kost
, Item
> > ItemList
;
30 typedef std::map
<Klass
, ItemList
> KlassItem
;
31 typedef std::map
<Prio
, KlassItem
> LQ
;
32 typedef std::list
<Item
> Removed
;
33 const unsigned max_prios
= 5; // (0-4) * 64
34 const unsigned klasses
= 37; // Make prime to help get good coverage
36 void fill_queue(WQ
&wq
, LQ
&strictq
, LQ
&normq
,
37 unsigned item_size
, bool randomize
= false) {
38 unsigned p
, k
, c
, o
, op_queue
, fob
;
39 for (unsigned i
= 1; i
<= item_size
; ++i
) {
40 // Choose priority, class, cost and 'op' for this op.
42 p
= (rand() % max_prios
) * 64;
44 c
= rand() % (1<<22); // 4M cost
45 // Make some of the costs 0, but make sure small costs
47 if (c
> (1<<19) && c
< (1<<20)) {
50 op_queue
= rand() % 10;
53 p
= (i
% max_prios
) * 64;
55 c
= (i
% 8 == 0 || i
% 16 == 0) ? 0 : 1 << (i
% 23);
56 op_queue
= i
% 7; // Use prime numbers to
57 fob
= i
% 11; // get better coverage
60 // Choose how to enqueue this op.
65 // Queue to the front.
66 strictq
[p
][k
].push_front(std::make_pair(
67 c
, std::make_tuple(p
, k
, o
)));
68 wq
.enqueue_strict_front(Klass(k
), p
, std::make_tuple(p
, k
, o
));
71 strictq
[p
][k
].push_back(std::make_pair(
72 c
, std::make_tuple(p
, k
, o
)));
73 wq
.enqueue_strict(Klass(k
), p
, std::make_tuple(p
, k
, o
));
79 // Queue to the front.
80 normq
[p
][k
].push_front(std::make_pair(
81 c
, std::make_tuple(p
, k
, o
)));
82 wq
.enqueue_front(Klass(k
), p
, c
, std::make_tuple(p
, k
, o
));
85 normq
[p
][k
].push_back(std::make_pair(
86 c
, std::make_tuple(p
, k
, o
)));
87 wq
.enqueue(Klass(k
), p
, c
, std::make_tuple(p
, k
, o
));
93 void test_queue(unsigned item_size
, bool randomize
= false) {
94 // Due to the WRR queue having a lot of probabilistic logic
95 // we can't determine the exact order OPs will be dequeued.
96 // However, the queue should not dequeue a priority out of
97 // order. It should also dequeue the strict priority queue
98 // first and in order. In both the strict and normal queues
99 // push front and back should be respected. Here we keep
100 // track of the ops queued and make sure they dequeue
103 // Set up local tracking queues
106 fill_queue(wq
, strictq
, normq
, item_size
, randomize
);
107 // Test that the queue is dequeuing properly.
108 typedef std::map
<unsigned, unsigned> LastKlass
;
109 LastKlass last_strict
, last_norm
;
110 while (!(wq
.empty())) {
111 Item r
= wq
.dequeue();
112 if (!(strictq
.empty())) {
113 // Check that there are no higher priorities
114 // in the strict queue.
115 LQ::reverse_iterator ri
= strictq
.rbegin();
116 EXPECT_EQ(std::get
<0>(r
), ri
->first
);
117 // Check that if there are multiple classes in a priority
118 // that it is not dequeueing the same class each time.
119 LastKlass::iterator si
= last_strict
.find(std::get
<0>(r
));
120 if (strictq
[std::get
<0>(r
)].size() > 1 && si
!= last_strict
.end()) {
121 EXPECT_NE(std::get
<1>(r
), si
->second
);
123 last_strict
[std::get
<0>(r
)] = std::get
<1>(r
);
125 Item t
= strictq
[std::get
<0>(r
)][std::get
<1>(r
)].front().second
;
126 EXPECT_EQ(std::get
<2>(r
), std::get
<2>(t
));
127 strictq
[std::get
<0>(r
)][std::get
<1>(r
)].pop_front();
128 if (strictq
[std::get
<0>(r
)][std::get
<1>(r
)].empty()) {
129 strictq
[std::get
<0>(r
)].erase(std::get
<1>(r
));
131 if (strictq
[std::get
<0>(r
)].empty()) {
132 strictq
.erase(std::get
<0>(r
));
135 // Check that if there are multiple classes in a priority
136 // that it is not dequeueing the same class each time.
137 LastKlass::iterator si
= last_norm
.find(std::get
<0>(r
));
138 if (normq
[std::get
<0>(r
)].size() > 1 && si
!= last_norm
.end()) {
139 EXPECT_NE(std::get
<1>(r
), si
->second
);
141 last_norm
[std::get
<0>(r
)] = std::get
<1>(r
);
143 Item t
= normq
[std::get
<0>(r
)][std::get
<1>(r
)].front().second
;
144 EXPECT_EQ(std::get
<2>(r
), std::get
<2>(t
));
145 normq
[std::get
<0>(r
)][std::get
<1>(r
)].pop_front();
146 if (normq
[std::get
<0>(r
)][std::get
<1>(r
)].empty()) {
147 normq
[std::get
<0>(r
)].erase(std::get
<1>(r
));
149 if (normq
[std::get
<0>(r
)].empty()) {
150 normq
.erase(std::get
<0>(r
));
156 void SetUp() override
{
159 void TearDown() override
{
163 TEST_F(WeightedPriorityQueueTest
, wpq_size
){
165 EXPECT_TRUE(wq
.empty());
166 EXPECT_EQ(0u, wq
.length());
168 // Test the strict queue size.
169 for (unsigned i
= 1; i
< 5; ++i
) {
170 wq
.enqueue_strict(Klass(i
),i
, std::make_tuple(i
, i
, i
));
171 EXPECT_FALSE(wq
.empty());
172 EXPECT_EQ(i
, wq
.length());
174 // Test the normal queue size.
175 for (unsigned i
= 5; i
< 10; ++i
) {
176 wq
.enqueue(Klass(i
), i
, i
, std::make_tuple(i
, i
, i
));
177 EXPECT_FALSE(wq
.empty());
178 EXPECT_EQ(i
, wq
.length());
180 // Test that as both queues are emptied
181 // the size is correct.
182 for (unsigned i
= 8; i
>0; --i
) {
184 EXPECT_FALSE(wq
.empty());
185 EXPECT_EQ(i
, wq
.length());
188 EXPECT_TRUE(wq
.empty());
189 EXPECT_EQ(0u, wq
.length());
192 TEST_F(WeightedPriorityQueueTest
, wpq_test_static
) {
196 TEST_F(WeightedPriorityQueueTest
, wpq_test_random
) {
197 test_queue(rand() % 500 + 500, true);
200 TEST_F(WeightedPriorityQueueTest
, wpq_test_remove_by_class_null
) {
203 unsigned num_items
= 10;
204 fill_queue(wq
, strictq
, normq
, num_items
);
206 // Pick a klass that was not enqueued
207 wq
.remove_by_class(klasses
+ 1, &wq_removed
);
208 EXPECT_EQ(0u, wq_removed
.size());
211 TEST_F(WeightedPriorityQueueTest
, wpq_test_remove_by_class
) {
214 unsigned num_items
= 1000;
215 fill_queue(wq
, strictq
, normq
, num_items
);
216 unsigned num_to_remove
= 0;
218 // Find how many ops are in the class
219 for (LQ::iterator it
= strictq
.begin();
220 it
!= strictq
.end(); ++it
) {
221 num_to_remove
+= it
->second
[k
].size();
223 for (LQ::iterator it
= normq
.begin();
224 it
!= normq
.end(); ++it
) {
225 num_to_remove
+= it
->second
[k
].size();
228 wq
.remove_by_class(k
, &wq_removed
);
229 // Check that the right ops were removed.
230 EXPECT_EQ(num_to_remove
, wq_removed
.size());
231 EXPECT_EQ(num_items
- num_to_remove
, wq
.length());
232 for (Removed::iterator it
= wq_removed
.begin();
233 it
!= wq_removed
.end(); ++it
) {
234 EXPECT_EQ(k
, std::get
<1>(*it
));
236 // Check that none were missed
237 while (!(wq
.empty())) {
238 EXPECT_NE(k
, std::get
<1>(wq
.dequeue()));