xref: /aosp_15_r20/system/chre/util/tests/transaction_manager_test.cc (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "chre/util/system/transaction_manager.h"
18 
19 #include <algorithm>
20 #include <map>
21 
22 #include "chre/core/event_loop_common.h"
23 #include "chre/core/timer_pool.h"
24 #include "chre/platform/linux/system_time.h"
25 
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28 
29 using chre::platform_linux::SystemTimeOverride;
30 using testing::_;
31 using testing::Return;
32 
33 namespace chre {
34 namespace {
35 
36 constexpr size_t kMaxTransactions = 32;
37 constexpr Nanoseconds kTimeout = Milliseconds(10);
38 constexpr uint16_t kMaxAttempts = 3;
39 
40 }  // anonymous namespace
41 
42 class MockTimerPool {
43  public:
44   MOCK_METHOD(TimerHandle, setSystemTimer,
45               (Nanoseconds, SystemEventCallbackFunction, SystemCallbackType,
46                void *));
47   MOCK_METHOD(bool, cancelSystemTimer, (TimerHandle));
48 };
49 
50 class FakeTimerPool {
51  public:
setSystemTimer(Nanoseconds duration,SystemEventCallbackFunction * callback,SystemCallbackType,void * data)52   TimerHandle setSystemTimer(Nanoseconds duration,
53                              SystemEventCallbackFunction *callback,
54                              SystemCallbackType /*callbackType*/, void *data) {
55     Timer timer = {
56         .expiry = SystemTime::getMonotonicTime() + duration,
57         .callback = callback,
58         .data = data,
59     };
60     TimerHandle handle = mNextHandle++;
61     mTimers[handle] = timer;
62     return handle;
63   }
cancelSystemTimer(TimerHandle handle)64   bool cancelSystemTimer(TimerHandle handle) {
65     return mTimers.erase(handle) == 1;
66   }
67 
68   //! Advance the time to the next expiring timer and invoke its callback
69   //! @return false if no timers exist
invokeNextTimer(SystemTimeOverride & time,Nanoseconds additionalDelay=Nanoseconds (0))70   bool invokeNextTimer(SystemTimeOverride &time,
71                        Nanoseconds additionalDelay = Nanoseconds(0)) {
72     auto it = std::min_element(mTimers.begin(), mTimers.end(),
73                                [](const auto &a, const auto &b) {
74                                  return a.second.expiry < b.second.expiry;
75                                });
76     if (it == mTimers.end()) {
77       return false;
78     }
79     Timer timer = it->second;
80     mTimers.erase(it);
81     time.update(timer.expiry + additionalDelay);
82     timer.callback(/*type=*/0, timer.data, /*extraData=*/nullptr);
83     return true;
84   }
85 
86   struct Timer {
87     Nanoseconds expiry;
88     SystemEventCallbackFunction *callback;
89     void *data;
90   };
91 
92   TimerHandle mNextHandle = 1;
93   std::map<TimerHandle, Timer> mTimers;
94 };
95 
96 class MockTransactionManagerCallback : public TransactionManagerCallback {
97  public:
98   MOCK_METHOD(void, onTransactionAttempt, (uint32_t, uint16_t), (override));
99   MOCK_METHOD(void, onTransactionFailure, (uint32_t, uint16_t), (override));
100 };
101 
102 class FakeTransactionManagerCallback : public TransactionManagerCallback {
103  public:
onTransactionAttempt(uint32_t transactionId,uint16_t)104   void onTransactionAttempt(uint32_t transactionId,
105                             uint16_t /*groupId*/) override {
106     mTries.push_back(transactionId);
107   }
onTransactionFailure(uint32_t transactionId,uint16_t)108   void onTransactionFailure(uint32_t transactionId,
109                             uint16_t /*groupId*/) override {
110     mFailures.push_back(transactionId);
111   }
112 
113   std::vector<uint32_t> mTries;
114   std::vector<uint32_t> mFailures;
115 };
116 
117 using TxnMgr = TransactionManager<kMaxTransactions, MockTimerPool>;
118 using TxnMgrF = TransactionManager<kMaxTransactions, FakeTimerPool>;
119 
120 class TransactionManagerTest : public testing::Test {
121  public:
122  protected:
defaultTxnMgr()123   TxnMgr defaultTxnMgr() {
124     return TxnMgr(mFakeCb, mTimerPool, kTimeout, kMaxAttempts);
125   }
126 
defaultTxnMgrF()127   TxnMgrF defaultTxnMgrF() {
128     return TxnMgrF(mFakeCb, mFakeTimerPool, kTimeout, kMaxAttempts);
129   }
130 
131   static constexpr uint32_t kTimerId = 1;
132 
133   MockTimerPool mTimerPool;
134   FakeTimerPool mFakeTimerPool;
135   FakeTransactionManagerCallback mFakeCb;
136   MockTransactionManagerCallback mMockCb;
137   SystemTimeOverride mTime = SystemTimeOverride(0);
138 };
139 
TEST_F(TransactionManagerTest,StartSingleTransaction)140 TEST_F(TransactionManagerTest, StartSingleTransaction) {
141   TxnMgr tm = defaultTxnMgr();
142 
143   EXPECT_CALL(mTimerPool, setSystemTimer(kTimeout, _, _, _))
144       .Times(1)
145       .WillOnce(Return(kTimerId));
146 
147   uint32_t id;
148   EXPECT_TRUE(tm.add(/*groupId=*/0, &id));
149 
150   ASSERT_EQ(mFakeCb.mTries.size(), 1);
151   EXPECT_EQ(mFakeCb.mTries[0], id);
152   EXPECT_EQ(mFakeCb.mFailures.size(), 0);
153 }
154 
TEST_F(TransactionManagerTest,RemoveSingleTransaction)155 TEST_F(TransactionManagerTest, RemoveSingleTransaction) {
156   TxnMgr tm = defaultTxnMgr();
157 
158   EXPECT_CALL(mTimerPool, setSystemTimer(_, _, _, _))
159       .Times(1)
160       .WillOnce(Return(kTimerId));
161 
162   uint32_t id;
163   ASSERT_TRUE(tm.add(/*groupId=*/0, &id));
164 
165   EXPECT_CALL(mTimerPool, cancelSystemTimer(kTimerId))
166       .Times(1)
167       .WillOnce(Return(true));
168 
169   EXPECT_TRUE(tm.remove(id));
170   EXPECT_EQ(mFakeCb.mTries.size(), 1);
171   EXPECT_EQ(mFakeCb.mFailures.size(), 0);
172 }
173 
TEST_F(TransactionManagerTest,SingleTransactionSuccessOnRetry)174 TEST_F(TransactionManagerTest, SingleTransactionSuccessOnRetry) {
175   TxnMgrF tm = defaultTxnMgrF();
176 
177   uint32_t id;
178   ASSERT_TRUE(tm.add(0, &id));
179   EXPECT_TRUE(mFakeTimerPool.invokeNextTimer(mTime));
180   EXPECT_EQ(mFakeCb.mTries.size(), 2);
181 
182   EXPECT_TRUE(tm.remove(id));
183   ASSERT_EQ(mFakeCb.mTries.size(), 2);
184   EXPECT_EQ(mFakeCb.mTries[0], id);
185   EXPECT_EQ(mFakeCb.mTries[1], id);
186   EXPECT_EQ(mFakeCb.mFailures.size(), 0);
187   EXPECT_FALSE(mFakeTimerPool.invokeNextTimer(mTime));
188 }
189 
TEST_F(TransactionManagerTest,SingleTransactionTimeout)190 TEST_F(TransactionManagerTest, SingleTransactionTimeout) {
191   TxnMgrF tm = defaultTxnMgrF();
192 
193   uint32_t id;
194   ASSERT_TRUE(tm.add(0, &id));
195   size_t count = 0;
196   while (mFakeTimerPool.invokeNextTimer(mTime) && count++ < kMaxAttempts * 2);
197   EXPECT_EQ(count, kMaxAttempts);
198   EXPECT_EQ(std::count(mFakeCb.mTries.begin(), mFakeCb.mTries.end(), id),
199             kMaxAttempts);
200   ASSERT_EQ(mFakeCb.mFailures.size(), 1);
201   EXPECT_EQ(mFakeCb.mFailures[0], id);
202 
203   // The transaction should actually be gone
204   EXPECT_FALSE(tm.remove(id));
205   EXPECT_FALSE(mFakeTimerPool.invokeNextTimer(mTime));
206 }
207 
TEST_F(TransactionManagerTest,TwoTransactionsDifferentGroups)208 TEST_F(TransactionManagerTest, TwoTransactionsDifferentGroups) {
209   TxnMgrF tm = defaultTxnMgrF();
210 
211   uint32_t id1;
212   uint32_t id2;
213   EXPECT_TRUE(tm.add(/*groupId=*/0, &id1));
214   EXPECT_TRUE(tm.add(/*groupId=*/1, &id2));
215 
216   // Both should start
217   ASSERT_EQ(mFakeCb.mTries.size(), 2);
218   EXPECT_EQ(mFakeCb.mTries[0], id1);
219   EXPECT_EQ(mFakeCb.mTries[1], id2);
220   EXPECT_EQ(mFakeCb.mFailures.size(), 0);
221 }
222 
TEST_F(TransactionManagerTest,TwoTransactionsSameGroup)223 TEST_F(TransactionManagerTest, TwoTransactionsSameGroup) {
224   TxnMgrF tm = defaultTxnMgrF();
225 
226   uint32_t id1;
227   uint32_t id2;
228   EXPECT_TRUE(tm.add(/*groupId=*/0, &id1));
229   EXPECT_TRUE(tm.add(/*groupId=*/0, &id2));
230 
231   // Only the first should start
232   ASSERT_EQ(mFakeCb.mTries.size(), 1);
233   EXPECT_EQ(mFakeCb.mTries[0], id1);
234 
235   // Second starts after the first finishes
236   EXPECT_TRUE(tm.remove(id1));
237   ASSERT_EQ(mFakeCb.mTries.size(), 2);
238   EXPECT_EQ(mFakeCb.mTries[1], id2);
239 
240   // Second completes with no funny business
241   EXPECT_TRUE(tm.remove(id2));
242   EXPECT_EQ(mFakeCb.mTries.size(), 2);
243   EXPECT_EQ(mFakeCb.mFailures.size(), 0);
244   EXPECT_FALSE(mFakeTimerPool.invokeNextTimer(mTime));
245 }
246 
TEST_F(TransactionManagerTest,TwoTransactionsSameGroupTimeout)247 TEST_F(TransactionManagerTest, TwoTransactionsSameGroupTimeout) {
248   TxnMgrF tm = defaultTxnMgrF();
249 
250   uint32_t id1;
251   uint32_t id2;
252   EXPECT_TRUE(tm.add(/*groupId=*/0, &id1));
253   EXPECT_TRUE(tm.add(/*groupId=*/0, &id2));
254 
255   // Time out the first transaction, which should kick off the second
256   for (size_t i = 0; i < kMaxAttempts; i++) {
257     EXPECT_TRUE(mFakeTimerPool.invokeNextTimer(mTime));
258   }
259   ASSERT_EQ(mFakeCb.mTries.size(), kMaxAttempts + 1);
260   EXPECT_EQ(std::count(mFakeCb.mTries.begin(), mFakeCb.mTries.end(), id1),
261             kMaxAttempts);
262   EXPECT_EQ(mFakeCb.mTries.back(), id2);
263 
264   // Retry + time out behavior for second works the same as the first
265   for (size_t i = 0; i < kMaxAttempts; i++) {
266     EXPECT_TRUE(mFakeTimerPool.invokeNextTimer(mTime));
267   }
268   ASSERT_EQ(mFakeCb.mTries.size(), kMaxAttempts * 2);
269   EXPECT_EQ(std::count(mFakeCb.mTries.begin(), mFakeCb.mTries.end(), id2),
270             kMaxAttempts);
271   ASSERT_EQ(mFakeCb.mFailures.size(), 2);
272   EXPECT_EQ(mFakeCb.mFailures[0], id1);
273   EXPECT_EQ(mFakeCb.mFailures[1], id2);
274   EXPECT_FALSE(mFakeTimerPool.invokeNextTimer(mTime));
275 }
276 
TEST_F(TransactionManagerTest,TwoTransactionsSameGroupRemoveReverseOrder)277 TEST_F(TransactionManagerTest, TwoTransactionsSameGroupRemoveReverseOrder) {
278   TxnMgrF tm = defaultTxnMgrF();
279 
280   uint32_t id1;
281   uint32_t id2;
282   EXPECT_TRUE(tm.add(/*groupId=*/0, &id1));
283   EXPECT_TRUE(tm.add(/*groupId=*/0, &id2));
284 
285   // Only the first should start
286   ASSERT_EQ(mFakeCb.mTries.size(), 1);
287   EXPECT_EQ(mFakeCb.mTries[0], id1);
288 
289   // Remove second one first
290   EXPECT_TRUE(tm.remove(id2));
291 
292   // Finish the first one
293   EXPECT_TRUE(tm.remove(id1));
294   ASSERT_EQ(mFakeCb.mTries.size(), 1);
295   EXPECT_EQ(mFakeCb.mTries[0], id1);
296   EXPECT_EQ(mFakeCb.mFailures.size(), 0);
297   EXPECT_FALSE(mFakeTimerPool.invokeNextTimer(mTime));
298 }
299 
TEST_F(TransactionManagerTest,MultipleTimeouts)300 TEST_F(TransactionManagerTest, MultipleTimeouts) {
301   TxnMgrF tm = defaultTxnMgrF();
302 
303   // Timeout both in a single callback
304   uint32_t ids[2];
305   EXPECT_TRUE(tm.add(/*groupId=*/0, &ids[0]));
306   mTime.update(kTimeout.toRawNanoseconds() / 2);
307   EXPECT_TRUE(tm.add(/*groupId=*/1, &ids[1]));
308   EXPECT_TRUE(mFakeTimerPool.invokeNextTimer(mTime, kTimeout));
309   EXPECT_EQ(mFakeCb.mTries.size(), 4);
310 
311   // Since both retries were dispatched at the same time, they should time out
312   // again together
313   EXPECT_TRUE(mFakeTimerPool.invokeNextTimer(mTime, kTimeout));
314   EXPECT_EQ(mFakeCb.mTries.size(), 6);
315 
316   // If changing the max # of attempts, modify the below code too so it triggers
317   // failure
318   static_assert(kMaxAttempts == 3);
319   EXPECT_TRUE(mFakeTimerPool.invokeNextTimer(mTime, kTimeout));
320   EXPECT_EQ(mFakeCb.mTries.size(), 6);
321   for (size_t i = 0; i < mFakeCb.mTries.size(); i++) {
322     EXPECT_EQ(mFakeCb.mTries[i], ids[i % 2]);
323   }
324   ASSERT_EQ(mFakeCb.mFailures.size(), 2);
325   EXPECT_EQ(mFakeCb.mFailures[0], ids[0]);
326   EXPECT_EQ(mFakeCb.mFailures[1], ids[1]);
327   EXPECT_FALSE(mFakeTimerPool.invokeNextTimer(mTime));
328 }
329 
TEST_F(TransactionManagerTest,CallbackUsesCorrectGroupId)330 TEST_F(TransactionManagerTest, CallbackUsesCorrectGroupId) {
331   TxnMgrF tm(mMockCb, mFakeTimerPool, kTimeout, /*maxAttempts=*/1);
332 
333   EXPECT_CALL(mMockCb, onTransactionAttempt(_, 1)).Times(1);
334   EXPECT_CALL(mMockCb, onTransactionAttempt(_, 2)).Times(1);
335   EXPECT_CALL(mMockCb, onTransactionAttempt(_, 3)).Times(1);
336 
337   uint32_t id;
338   tm.add(1, &id);
339   tm.add(2, &id);
340   tm.add(3, &id);
341 
342   EXPECT_CALL(mMockCb, onTransactionFailure(_, 1)).Times(1);
343   EXPECT_CALL(mMockCb, onTransactionFailure(_, 2)).Times(1);
344   EXPECT_CALL(mMockCb, onTransactionFailure(_, 3)).Times(1);
345 
346   mFakeTimerPool.invokeNextTimer(mTime);
347   mFakeTimerPool.invokeNextTimer(mTime);
348   mFakeTimerPool.invokeNextTimer(mTime);
349 }
350 
351 }  // namespace chre
352