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