xref: /aosp_15_r20/external/grpc-grpc/test/core/event_engine/posix/timer_heap_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include "src/core/lib/event_engine/posix_engine/timer_heap.h"
20 
21 #include <stdint.h>
22 #include <stdlib.h>
23 
24 #include <algorithm>
25 #include <utility>
26 
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29 
30 #include <grpc/support/log.h>
31 
32 #include "src/core/lib/event_engine/posix_engine/timer.h"
33 #include "src/core/lib/gprpp/bitset.h"
34 
35 using testing::Contains;
36 using testing::Not;
37 
38 namespace grpc_event_engine {
39 namespace experimental {
40 
41 namespace {
RandomDeadline(void)42 int64_t RandomDeadline(void) { return rand(); }
43 
CreateTestElements(size_t num_elements)44 std::vector<Timer> CreateTestElements(size_t num_elements) {
45   std::vector<Timer> elems(num_elements);
46   for (size_t i = 0; i < num_elements; i++) {
47     elems[i].deadline = RandomDeadline();
48   }
49   return elems;
50 }
51 
CheckValid(TimerHeap * pq)52 void CheckValid(TimerHeap* pq) {
53   const std::vector<Timer*>& timers = pq->TestOnlyGetTimers();
54   for (size_t i = 0; i < timers.size(); ++i) {
55     size_t left_child = 1u + 2u * i;
56     size_t right_child = left_child + 1u;
57     if (left_child < timers.size()) {
58       EXPECT_LE(timers[i]->deadline, timers[left_child]->deadline);
59     }
60     if (right_child < timers.size()) {
61       EXPECT_LE(timers[i]->deadline, timers[right_child]->deadline);
62     }
63   }
64 }
65 
TEST(TimerHeapTest,Basics)66 TEST(TimerHeapTest, Basics) {
67   TimerHeap pq;
68   const size_t num_test_elements = 200;
69   const size_t num_test_operations = 10000;
70   size_t i;
71   std::vector<Timer> test_elements = CreateTestElements(num_test_elements);
72   grpc_core::BitSet<num_test_elements> inpq;
73 
74   EXPECT_TRUE(pq.is_empty());
75   CheckValid(&pq);
76   for (i = 0; i < num_test_elements; ++i) {
77     EXPECT_THAT(pq.TestOnlyGetTimers(), Not(Contains(&test_elements[i])));
78     pq.Add(&test_elements[i]);
79     CheckValid(&pq);
80     EXPECT_THAT(pq.TestOnlyGetTimers(), Contains(&test_elements[i]));
81     inpq.set(i);
82   }
83   for (i = 0; i < num_test_elements; ++i) {
84     // Test that check still succeeds even for element that wasn't just
85     // inserted.
86     EXPECT_THAT(pq.TestOnlyGetTimers(), Contains(&test_elements[i]));
87   }
88 
89   EXPECT_EQ(pq.TestOnlyGetTimers().size(), num_test_elements);
90   CheckValid(&pq);
91 
92   for (i = 0; i < num_test_operations; ++i) {
93     size_t elem_num = static_cast<size_t>(rand()) % num_test_elements;
94     Timer* el = &test_elements[elem_num];
95     if (!inpq.is_set(elem_num)) {  // not in pq
96       EXPECT_THAT(pq.TestOnlyGetTimers(), Not(Contains(el)));
97       el->deadline = RandomDeadline();
98       pq.Add(el);
99       EXPECT_THAT(pq.TestOnlyGetTimers(), Contains(el));
100       inpq.set(elem_num);
101       CheckValid(&pq);
102     } else {
103       EXPECT_THAT(pq.TestOnlyGetTimers(), Contains(el));
104       pq.Remove(el);
105       EXPECT_THAT(pq.TestOnlyGetTimers(), Not(Contains(el)));
106       inpq.clear(elem_num);
107       CheckValid(&pq);
108     }
109   }
110 }
111 
112 struct ElemStruct {
113   Timer elem;
114   bool inserted = false;
115 };
116 
SearchElems(std::vector<ElemStruct> & elems,bool inserted)117 ElemStruct* SearchElems(std::vector<ElemStruct>& elems, bool inserted) {
118   std::vector<size_t> search_order;
119   for (size_t i = 0; i < elems.size(); i++) {
120     search_order.push_back(i);
121   }
122   for (size_t i = 0; i < elems.size() * 2; i++) {
123     size_t a = static_cast<size_t>(rand()) % elems.size();
124     size_t b = static_cast<size_t>(rand()) % elems.size();
125     std::swap(search_order[a], search_order[b]);
126   }
127   ElemStruct* out = nullptr;
128   for (size_t i = 0; out == nullptr && i < elems.size(); i++) {
129     if (elems[search_order[i]].inserted == inserted) {
130       out = &elems[search_order[i]];
131     }
132   }
133   return out;
134 }
135 
136 // TODO(ctiller): this should be an actual fuzzer
TEST(TimerHeapTest,RandomMutations)137 TEST(TimerHeapTest, RandomMutations) {
138   TimerHeap pq;
139 
140   static const size_t elems_size = 1000;
141   std::vector<ElemStruct> elems(elems_size);
142   size_t num_inserted = 0;
143 
144   for (size_t round = 0; round < 10000; round++) {
145     int r = rand() % 1000;
146     if (r <= 550) {
147       // 55% of the time we try to add something
148       ElemStruct* el = SearchElems(elems, false);
149       if (el != nullptr) {
150         el->elem.deadline = RandomDeadline();
151         pq.Add(&el->elem);
152         el->inserted = true;
153         num_inserted++;
154         CheckValid(&pq);
155       }
156     } else if (r <= 650) {
157       // 10% of the time we try to remove something
158       ElemStruct* el = SearchElems(elems, true);
159       if (el != nullptr) {
160         pq.Remove(&el->elem);
161         el->inserted = false;
162         num_inserted--;
163         CheckValid(&pq);
164       }
165     } else {
166       // the remaining times we pop
167       if (num_inserted > 0) {
168         Timer* top = pq.Top();
169         pq.Pop();
170         for (size_t i = 0; i < elems_size; i++) {
171           if (top == &elems[i].elem) {
172             GPR_ASSERT(elems[i].inserted);
173             elems[i].inserted = false;
174           }
175         }
176         num_inserted--;
177         CheckValid(&pq);
178       }
179     }
180 
181     if (num_inserted) {
182       int64_t* min_deadline = nullptr;
183       for (size_t i = 0; i < elems_size; i++) {
184         if (elems[i].inserted) {
185           if (min_deadline == nullptr) {
186             min_deadline = &elems[i].elem.deadline;
187           } else {
188             if (elems[i].elem.deadline < *min_deadline) {
189               min_deadline = &elems[i].elem.deadline;
190             }
191           }
192         }
193       }
194       GPR_ASSERT(pq.Top()->deadline == *min_deadline);
195     }
196   }
197 }
198 
199 }  // namespace
200 }  // namespace experimental
201 }  // namespace grpc_event_engine
202 
main(int argc,char ** argv)203 int main(int argc, char** argv) {
204   ::testing::InitGoogleTest(&argc, argv);
205   return RUN_ALL_TESTS();
206 }
207