• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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