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