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 <chrono>
18 #include <condition_variable>
19 #include <cstdint>
20 #include <map>
21 #include <mutex>
22 #include <optional>
23 
24 #include "chre/platform/linux/task_util/task_manager.h"
25 #include "chre/util/nested_data_ptr.h"
26 #include "chre/util/time.h"
27 #include "chre/util/transaction_manager.h"
28 
29 #include "gtest/gtest.h"
30 
31 namespace chre {
32 namespace {
33 
34 constexpr uint16_t kMaxNumRetries = 3;
35 constexpr size_t kMaxTransactions = 32;
36 constexpr Milliseconds kRetryWaitTime = Milliseconds(10);
37 constexpr Milliseconds kTransactionTimeout = Milliseconds(100);
38 constexpr std::chrono::milliseconds kWaitTimeout =
39     std::chrono::milliseconds(500);
40 
41 class TransactionManagerTest;
42 
43 struct TransactionData {
44   TransactionManagerTest *test;
45   bool *transactionStarted;
46   uint32_t *numTimesTransactionStarted;
47   uint32_t data;
48 };
49 
50 struct TransactionCompleted {
51   TransactionData data;
52   uint8_t errorCode;
53 };
54 
55 class TransactionManagerTest : public testing::Test {
56  protected:
transactionStartCallback(TransactionData & data,bool doFaultyStart)57   bool transactionStartCallback(TransactionData &data, bool doFaultyStart) {
58     bool faultyStartSuccess = true;
59     {
60       std::lock_guard<std::mutex> lock(mMutex);
61 
62       if (data.transactionStarted != nullptr) {
63         *data.transactionStarted = true;
64       }
65 
66       if (data.numTimesTransactionStarted != nullptr) {
67         ++(*data.numTimesTransactionStarted);
68         faultyStartSuccess = *data.numTimesTransactionStarted > 1;
69       }
70     }
71 
72     bool success = !doFaultyStart || faultyStartSuccess;
73     if (success) {
74       mCondVar.notify_all();
75     }
76     return success;
77   }
78 
transactionCallback(const TransactionData & data,uint8_t errorCode)79   bool transactionCallback(const TransactionData &data, uint8_t errorCode) {
80     {
81       std::lock_guard<std::mutex> lock(mMutex);
82 
83       EXPECT_FALSE(mTransactionCallbackCalled);
84       mTransactionCallbackCalled = true;
85       mTransactionCompleted.data = data;
86       mTransactionCompleted.errorCode = errorCode;
87     }
88 
89     mCondVar.notify_all();
90     return true;
91   }
92 
deferCallback(TransactionManager<TransactionData,kMaxTransactions>::DeferCallbackFunction func,void * data,void * extraData,Nanoseconds delay,uint32_t * outTimerHandle)93   static bool deferCallback(
94       TransactionManager<TransactionData,
95                          kMaxTransactions>::DeferCallbackFunction func,
96       void *data, void *extraData, Nanoseconds delay,
97       uint32_t *outTimerHandle) {
98     if (func == nullptr) {
99       return false;
100     }
101 
102     const TransactionManagerTest *test = nullptr;
103     {
104       std::lock_guard<std::mutex> lock(sMapMutex);
105       auto iter = sMap.find(getTestName());
106       if (iter == sMap.end()) {
107         if (outTimerHandle != nullptr) {
108           *outTimerHandle = 0xDEADBEEF;
109         }
110         return true;  // Test is ending - no need to defer callback
111       }
112       test = iter->second;
113     }
114 
115     std::optional<uint32_t> taskId = test->getTaskManager()->addTask(
116         [func, data, extraData]() { func(/* type= */ 0, data, extraData); },
117         std::chrono::nanoseconds(delay.toRawNanoseconds()),
118         /* isOneShot= */ true);
119 
120     if (!taskId.has_value()) {
121       return false;
122     }
123 
124     if (outTimerHandle != nullptr) {
125       *outTimerHandle = *taskId;
126     }
127     return true;
128   }
129 
deferCancelCallback(uint32_t timerHandle)130   static bool deferCancelCallback(uint32_t timerHandle) {
131     const TransactionManagerTest *test = nullptr;
132     {
133       std::lock_guard<std::mutex> lock(sMapMutex);
134       auto iter = sMap.find(getTestName());
135       if (iter == sMap.end()) {
136         return true;  // Test is ending - no need to cancel defer callback
137       }
138       test = iter->second;
139     }
140 
141     return test->getTaskManager()->cancelTask(timerHandle);
142   }
143 
getTestName()144   static std::string getTestName() {
145     std::string testName;
146     auto instance = testing::UnitTest::GetInstance();
147     if (instance != nullptr) {
148       auto testInfo = instance->current_test_info();
149       if (testInfo != nullptr) {
150         testName = testInfo->name();
151       }
152     }
153     return testName;
154   }
155 
getTaskManager() const156   TaskManager *getTaskManager() const {
157     EXPECT_NE(mTaskManager.get(), nullptr);
158     return mTaskManager.get();
159   }
160 
161   std::unique_ptr<TransactionManager<TransactionData, kMaxTransactions>>
getTransactionManager(bool doFaultyStart,uint16_t maxNumRetries=kMaxNumRetries) const162   getTransactionManager(bool doFaultyStart,
163                         uint16_t maxNumRetries = kMaxNumRetries) const {
164     return std::make_unique<TransactionManager<TransactionData, kMaxTransactions>>(
165         doFaultyStart
166             ? [](TransactionData &data) {
167               return data.test != nullptr &&
168                   data.test->transactionStartCallback(data,
169                       /* doFaultyStart= */ true);
170             }
171             : [](TransactionData &data) {
172               return data.test != nullptr &&
173                   data.test->transactionStartCallback(data,
174                       /* doFaultyStart= */ false);
175             },
176         [](const TransactionData &data, uint8_t errorCode) {
177           return data.test != nullptr &&
178               data.test->transactionCallback(data, errorCode);
179         },
180         TransactionManagerTest::deferCallback,
181         TransactionManagerTest::deferCancelCallback,
182         kRetryWaitTime,
183         kTransactionTimeout,
184         maxNumRetries);
185   }
186 
SetUp()187   void SetUp() override {
188     {
189       std::lock_guard<std::mutex> lock(sMapMutex);
190       std::string testName = getTestName();
191       ASSERT_FALSE(testName.empty());
192       sMap.insert_or_assign(testName, this);
193     }
194 
195     mTransactionManager = getTransactionManager(/* doFaultyStart= */ false);
196     mFaultyStartTransactionManager =
197         getTransactionManager(/* doFaultyStart= */ true);
198     mZeroRetriesTransactionManager =
199         getTransactionManager(/* doFaultyStart= */ false,
200                               /* maxNumRetries= */ 0);
201 
202     mTaskManager = std::make_unique<TaskManager>();
203   }
204 
TearDown()205   void TearDown() override {
206     {
207       std::lock_guard<std::mutex> lock(sMapMutex);
208       std::string testName = getTestName();
209       ASSERT_FALSE(testName.empty());
210       sMap.erase(testName);
211     }
212 
213     mTaskManager->flushAndStop();
214     mTaskManager.reset();
215     mZeroRetriesTransactionManager.reset();
216     mFaultyStartTransactionManager.reset();
217     mTransactionManager.reset();
218   }
219 
220   static std::mutex sMapMutex;
221   static std::map<std::string, const TransactionManagerTest *> sMap;
222 
223   std::mutex mMutex;
224   std::condition_variable mCondVar;
225   bool mTransactionCallbackCalled = false;
226   std::unique_ptr<TaskManager> mTaskManager = nullptr;
227   TransactionCompleted mTransactionCompleted;
228 
229   std::unique_ptr<TransactionManager<TransactionData, kMaxTransactions>>
230       mTransactionManager = nullptr;
231   std::unique_ptr<TransactionManager<TransactionData, kMaxTransactions>>
232       mFaultyStartTransactionManager = nullptr;
233   std::unique_ptr<TransactionManager<TransactionData, kMaxTransactions>>
234       mZeroRetriesTransactionManager = nullptr;
235 };
236 std::mutex TransactionManagerTest::sMapMutex;
237 std::map<std::string, const TransactionManagerTest *>
238     TransactionManagerTest::sMap;
239 
TEST_F(TransactionManagerTest,TransactionShouldComplete)240 TEST_F(TransactionManagerTest, TransactionShouldComplete) {
241   std::unique_lock<std::mutex> lock(mMutex);
242 
243   bool transactionStarted1 = false;
244   bool transactionStarted2 = false;
245   uint32_t transactionId1;
246   uint32_t transactionId2;
247   EXPECT_TRUE(mTransactionManager->startTransaction(
248       {
249           .test = this,
250           .transactionStarted = &transactionStarted1,
251           .numTimesTransactionStarted = nullptr,
252           .data = 1,
253       },
254       /* cookie= */ 1, &transactionId1));
255   mCondVar.wait_for(lock, kWaitTimeout,
256                     [&transactionStarted1]() { return transactionStarted1; });
257   EXPECT_TRUE(transactionStarted1);
258 
259   EXPECT_TRUE(mTransactionManager->startTransaction(
260       {
261           .test = this,
262           .transactionStarted = &transactionStarted2,
263           .numTimesTransactionStarted = nullptr,
264           .data = 2,
265       },
266       /* cookie= */ 2, &transactionId2));
267   mCondVar.wait_for(lock, kWaitTimeout,
268                     [&transactionStarted2]() { return transactionStarted2; });
269   EXPECT_TRUE(transactionStarted2);
270 
271   mTransactionCallbackCalled = false;
272   EXPECT_TRUE(mTransactionManager->completeTransaction(
273       transactionId2, CHRE_ERROR_INVALID_ARGUMENT));
274   mCondVar.wait_for(lock, kWaitTimeout,
275                     [this]() { return mTransactionCallbackCalled; });
276   EXPECT_TRUE(mTransactionCallbackCalled);
277   EXPECT_EQ(mTransactionCompleted.data.data, 2);
278   EXPECT_EQ(mTransactionCompleted.errorCode, CHRE_ERROR_INVALID_ARGUMENT);
279 
280   mTransactionCallbackCalled = false;
281   EXPECT_TRUE(mTransactionManager->completeTransaction(transactionId1,
282                                                        CHRE_ERROR_NONE));
283   mCondVar.wait_for(lock, kWaitTimeout,
284                     [this]() { return mTransactionCallbackCalled; });
285   EXPECT_TRUE(mTransactionCallbackCalled);
286   EXPECT_EQ(mTransactionCompleted.data.data, 1);
287   EXPECT_EQ(mTransactionCompleted.errorCode, CHRE_ERROR_NONE);
288 }
289 
TEST_F(TransactionManagerTest,TransactionShouldCompleteOnlyOnce)290 TEST_F(TransactionManagerTest, TransactionShouldCompleteOnlyOnce) {
291   std::unique_lock<std::mutex> lock(mMutex);
292 
293   uint32_t transactionId;
294   bool transactionStarted = false;
295   EXPECT_TRUE(mTransactionManager->startTransaction(
296       {
297           .test = this,
298           .transactionStarted = &transactionStarted,
299           .numTimesTransactionStarted = nullptr,
300           .data = 1,
301       },
302       /* cookie= */ 1, &transactionId));
303   mCondVar.wait_for(lock, kWaitTimeout,
304                     [&transactionStarted]() { return transactionStarted; });
305   EXPECT_TRUE(transactionStarted);
306 
307   mTransactionCallbackCalled = false;
308   EXPECT_TRUE(mTransactionManager->completeTransaction(
309       transactionId, CHRE_ERROR_INVALID_ARGUMENT));
310   mCondVar.wait_for(lock, kWaitTimeout,
311                     [this]() { return mTransactionCallbackCalled; });
312   EXPECT_TRUE(mTransactionCallbackCalled);
313 
314   mTransactionCallbackCalled = false;
315   EXPECT_FALSE(mTransactionManager->completeTransaction(
316       transactionId, CHRE_ERROR_INVALID_ARGUMENT));
317   EXPECT_FALSE(mTransactionCallbackCalled);
318 }
319 
TEST_F(TransactionManagerTest,TransactionShouldTimeout)320 TEST_F(TransactionManagerTest, TransactionShouldTimeout) {
321   std::unique_lock<std::mutex> lock(mMutex);
322 
323   uint32_t numTimesTransactionStarted = 0;
324   uint32_t transactionId;
325   mTransactionCallbackCalled = false;
326   EXPECT_TRUE(mTransactionManager->startTransaction(
327       {
328           .test = this,
329           .transactionStarted = nullptr,
330           .numTimesTransactionStarted = &numTimesTransactionStarted,
331           .data = 456,
332       },
333       /* cookie= */ 1, &transactionId));
334 
335   mTransactionCallbackCalled = false;
336   mCondVar.wait_for(lock, kWaitTimeout,
337                     [this]() { return mTransactionCallbackCalled; });
338   EXPECT_TRUE(mTransactionCallbackCalled);
339   EXPECT_EQ(mTransactionCompleted.data.data, 456);
340   EXPECT_EQ(mTransactionCompleted.errorCode, CHRE_ERROR_TIMEOUT);
341   EXPECT_EQ(numTimesTransactionStarted, kMaxNumRetries + 1);
342 }
343 
TEST_F(TransactionManagerTest,TransactionShouldRetryWhenTransactCallbackFails)344 TEST_F(TransactionManagerTest,
345        TransactionShouldRetryWhenTransactCallbackFails) {
346   std::unique_lock<std::mutex> lock(mMutex);
347 
348   uint32_t numTimesTransactionStarted = 0;
349   const NestedDataPtr<uint32_t> kData(456);
350   uint32_t transactionId;
351   mTransactionCallbackCalled = false;
352   EXPECT_TRUE(mFaultyStartTransactionManager->startTransaction(
353       {
354           .test = this,
355           .transactionStarted = nullptr,
356           .numTimesTransactionStarted = &numTimesTransactionStarted,
357           .data = kData,
358       },
359       /* cookie= */ 1, &transactionId));
360   mCondVar.wait_for(lock, kWaitTimeout,
361                     [&numTimesTransactionStarted]() {
362     return numTimesTransactionStarted >= 2;
363   });
364   EXPECT_GE(numTimesTransactionStarted, 2);
365 
366   mTransactionCallbackCalled = false;
367   EXPECT_TRUE(mFaultyStartTransactionManager->completeTransaction(
368       transactionId, CHRE_ERROR_NONE));
369   mCondVar.wait_for(lock, kWaitTimeout,
370                     [this]() { return mTransactionCallbackCalled; });
371   EXPECT_TRUE(mTransactionCallbackCalled);
372   EXPECT_EQ(mTransactionCompleted.data.data, 456);
373   EXPECT_EQ(mTransactionCompleted.errorCode, CHRE_ERROR_NONE);
374 }
375 
TEST_F(TransactionManagerTest,TransactionShouldTimeoutWithNoRetries)376 TEST_F(TransactionManagerTest, TransactionShouldTimeoutWithNoRetries) {
377   std::unique_lock<std::mutex> lock(mMutex);
378 
379   uint32_t numTimesTransactionStarted = 0;
380   uint32_t transactionId;
381   mTransactionCallbackCalled = false;
382   EXPECT_TRUE(mZeroRetriesTransactionManager->startTransaction(
383       {
384           .test = this,
385           .transactionStarted = nullptr,
386           .numTimesTransactionStarted = &numTimesTransactionStarted,
387           .data = 456,
388       },
389       /* cookie= */ 1, &transactionId));
390 
391   mTransactionCallbackCalled = false;
392   mCondVar.wait_for(lock, kWaitTimeout,
393                     [this]() { return mTransactionCallbackCalled; });
394   EXPECT_TRUE(mTransactionCallbackCalled);
395   EXPECT_EQ(mTransactionCompleted.data.data, 456);
396   EXPECT_EQ(mTransactionCompleted.errorCode, CHRE_ERROR_TIMEOUT);
397   EXPECT_EQ(numTimesTransactionStarted, 1);  // No retries - only called once
398 }
399 
TEST_F(TransactionManagerTest,FlushedTransactionShouldNotComplete)400 TEST_F(TransactionManagerTest, FlushedTransactionShouldNotComplete) {
401   std::unique_lock<std::mutex> lock(mMutex);
402 
403   bool transactionStarted1 = false;
404   bool transactionStarted2 = false;
405   uint32_t transactionId1;
406   uint32_t transactionId2;
407   EXPECT_TRUE(mTransactionManager->startTransaction(
408       {
409           .test = this,
410           .transactionStarted = &transactionStarted1,
411           .numTimesTransactionStarted = nullptr,
412           .data = 1,
413       },
414       /* cookie= */ 1, &transactionId1));
415   mCondVar.wait_for(lock, kWaitTimeout,
416                     [&transactionStarted1]() { return transactionStarted1; });
417   EXPECT_TRUE(transactionStarted1);
418 
419   EXPECT_TRUE(mTransactionManager->startTransaction(
420       {
421           .test = this,
422           .transactionStarted = &transactionStarted2,
423           .numTimesTransactionStarted = nullptr,
424           .data = 2,
425       },
426       /* cookie= */ 2, &transactionId2));
427   mCondVar.wait_for(lock, kWaitTimeout,
428                     [&transactionStarted2]() { return transactionStarted2; });
429   EXPECT_TRUE(transactionStarted2);
430 
431   EXPECT_EQ(mTransactionManager->flushTransactions(
432                 [](const TransactionData &data, void *callbackData) {
433                   NestedDataPtr<uint32_t> magicNum(callbackData);
434                   return magicNum == 456 && data.data == 2;
435                 },
436                 NestedDataPtr<uint32_t>(456)),
437             1);
438 
439   EXPECT_FALSE(mTransactionManager->completeTransaction(
440       transactionId2, CHRE_ERROR_INVALID_ARGUMENT));
441 
442   mTransactionCallbackCalled = false;
443   EXPECT_TRUE(mTransactionManager->completeTransaction(transactionId1,
444                                                        CHRE_ERROR_NONE));
445   mCondVar.wait_for(lock, kWaitTimeout,
446                     [this]() { return mTransactionCallbackCalled; });
447   EXPECT_TRUE(mTransactionCallbackCalled);
448   EXPECT_EQ(mTransactionCompleted.data.data, 1);
449   EXPECT_EQ(mTransactionCompleted.errorCode, CHRE_ERROR_NONE);
450 }
451 
TEST_F(TransactionManagerTest,TransactionShouldWaitSameCookie)452 TEST_F(TransactionManagerTest, TransactionShouldWaitSameCookie) {
453   std::unique_lock<std::mutex> lock(mMutex);
454 
455   bool transactionStarted1 = false;
456   bool transactionStarted2 = false;
457   uint32_t transactionId1;
458   uint32_t transactionId2;
459   EXPECT_TRUE(mTransactionManager->startTransaction(
460       {
461           .test = this,
462           .transactionStarted = &transactionStarted1,
463           .numTimesTransactionStarted = nullptr,
464           .data = 1,
465       },
466       /* cookie= */ 0xCAFE, &transactionId1));
467   EXPECT_TRUE(mTransactionManager->startTransaction(
468       {
469           .test = this,
470           .transactionStarted = &transactionStarted2,
471           .numTimesTransactionStarted = nullptr,
472           .data = 2,
473       },
474       /* cookie= */ 0xCAFE, &transactionId2));
475   mCondVar.wait_for(lock, kWaitTimeout,
476                     [&transactionStarted1]() { return transactionStarted1; });
477   EXPECT_TRUE(transactionStarted1);
478   EXPECT_FALSE(transactionStarted2);
479 
480   mTransactionCallbackCalled = false;
481   EXPECT_TRUE(mTransactionManager->completeTransaction(
482       transactionId1, CHRE_ERROR_INVALID_ARGUMENT));
483   mCondVar.wait_for(lock, kWaitTimeout,
484                     [this, &transactionStarted2]() {
485                       return mTransactionCallbackCalled && transactionStarted2;
486                     });
487   EXPECT_TRUE(mTransactionCallbackCalled);
488   EXPECT_EQ(mTransactionCompleted.data.data, 1);
489   EXPECT_EQ(mTransactionCompleted.errorCode, CHRE_ERROR_INVALID_ARGUMENT);
490   EXPECT_TRUE(transactionStarted2);
491 
492   mTransactionCallbackCalled = false;
493   EXPECT_TRUE(mTransactionManager->completeTransaction(transactionId2,
494                                                        CHRE_ERROR_NONE));
495   mCondVar.wait_for(lock, kWaitTimeout,
496                     [this]() { return mTransactionCallbackCalled; });
497   EXPECT_TRUE(mTransactionCallbackCalled);
498   EXPECT_EQ(mTransactionCompleted.data.data, 2);
499   EXPECT_EQ(mTransactionCompleted.errorCode, CHRE_ERROR_NONE);
500 }
501 
502 }  // namespace
503 }  // namespace chre
504