test-scheduler-blink-example.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. // test-scheduler-blink-example.cpp - TaskScheduler Blink Example Validation Tests
  2. // This file contains comprehensive tests validating the TaskScheduler Blink example functionality
  3. // Based on examples\Scheduler_example00_Blink\ demonstrating various LED blinking approaches
  4. //
  5. // =====================================================================================
  6. // BLINK EXAMPLE TEST PLAN AND COVERAGE MATRIX
  7. // =====================================================================================
  8. //
  9. // PURPOSE: Validate all blink example patterns and TaskScheduler methods used in real applications
  10. // APPROACH: Simulate LED state changes and timing patterns without actual hardware
  11. // SCOPE: All six blink approaches from the reference example with comprehensive verification
  12. //
  13. // COVERAGE MATRIX:
  14. // ================
  15. //
  16. // 1. APPROACH 1 - SIMPLE FLAG DRIVEN BLINKING
  17. // ├── Boolean State Management
  18. // │ ├── LED state toggling logic
  19. // │ ├── isFirstIteration() detection
  20. // │ └── isLastIteration() cleanup
  21. // ├── Task Lifecycle
  22. // │ ├── Auto-enabled task execution
  23. // │ ├── Fixed interval timing (500ms)
  24. // │ └── Limited iteration count (20 cycles for 10 seconds)
  25. // └── Task Chaining
  26. // └── Delayed restart of next task pattern
  27. //
  28. // 2. APPROACH 2 - DUAL CALLBACK METHOD SWITCHING
  29. // ├── Dynamic Callback Switching
  30. // │ ├── setCallback() method usage
  31. // │ ├── ON callback registration
  32. // │ └── OFF callback registration
  33. // ├── Callback Coordination
  34. // │ ├── State transition between callbacks
  35. // │ ├── First/last iteration handling in both callbacks
  36. // │ └── Proper LED state management
  37. // └── Task Chain Management
  38. // └── Sequential task activation with delays
  39. //
  40. // 3. APPROACH 3 - RUN COUNTER DRIVEN BLINKING
  41. // ├── Run Counter Logic
  42. // │ ├── getRunCounter() method usage
  43. // │ ├── Odd/even counter state detection
  44. // │ └── Counter-based LED state determination
  45. // ├── Task State Management
  46. // │ ├── First iteration detection
  47. // │ ├── Last iteration handling
  48. // │ └── Task chain progression
  49. // └── OnEnable Callback Registration
  50. // └── Dynamic setOnEnable() assignment
  51. //
  52. // 4. APPROACH 4 - STATUS REQUEST COORDINATION
  53. // ├── StatusRequest Objects
  54. // │ ├── getInternalStatusRequest() usage
  55. // │ ├── waitForDelayed() coordination
  56. // │ └── Inter-task communication patterns
  57. // ├── OnEnable/OnDisable Callbacks
  58. // │ ├── OnEnable callback setup and removal
  59. // │ ├── OnDisable callback execution
  60. // │ └── Task lifecycle management
  61. // ├── Task Coordination
  62. // │ ├── Two-task ping-pong pattern
  63. // │ ├── Counter-based termination
  64. // │ └── Manual task disable() calls
  65. // └── Advanced Task Control
  66. // └── Delayed restart with offset timing
  67. //
  68. // 5. APPROACH 5 - INTERLEAVING TASKS
  69. // ├── Dual Task Pattern
  70. // │ ├── Independent task scheduling
  71. // │ ├── Phase-shifted execution (300ms offset)
  72. // │ └── Synchronized start/stop
  73. // ├── OnEnable Management
  74. // │ ├── One-time OnEnable callback
  75. // │ ├── Callback removal after first use
  76. // │ └── Task state coordination
  77. // └── OnDisable Coordination
  78. // └── Task chain progression from OnDisable
  79. //
  80. // 6. APPROACH 6 - RANDOM INTERVAL GENERATION
  81. // ├── Dynamic Interval Modification
  82. // │ ├── setInterval() runtime changes
  83. // │ ├── Random interval generation
  84. // │ └── Complementary timing calculation
  85. // ├── Run Counter + Interval Combination
  86. // │ ├── Counter-based state logic
  87. // │ ├── Interval adjustment per state
  88. // │ └── Total period maintenance
  89. // ├── OnEnable Setup
  90. // │ ├── Random interval calculation
  91. // │ ├── Initial task delay
  92. // │ └── Callback removal
  93. // └── Circular Task Pattern
  94. // └── Return to first approach (endless loop simulation)
  95. //
  96. // INTEGRATION TESTS:
  97. // ==================
  98. //
  99. // 7. TASK CHAINING VALIDATION
  100. // ├── Sequential Execution Pattern
  101. // │ ├── Approach 1 → 2 → 3 → 4 → 5 → 6 → 1 (loop)
  102. // │ ├── 2-second gaps between approaches
  103. // │ └── Clean state transitions
  104. // ├── Timing Verification
  105. // │ ├── 10-second duration per approach
  106. // │ ├── Proper interval execution
  107. // │ └── Delay accuracy
  108. // └── State Management
  109. // ├── LED state consistency
  110. // ├── Proper cleanup between approaches
  111. // └── Task enable/disable coordination
  112. //
  113. // 8. SCHEDULER EXECUTION VALIDATION
  114. // ├── Core Scheduler Methods
  115. // │ ├── execute() return values
  116. // │ ├── Task registration handling
  117. // │ └── Execution order verification
  118. // ├── Advanced Features Integration
  119. // │ ├── STATUS_REQUEST functionality
  120. // │ ├── OnEnable/OnDisable callbacks
  121. // │ └── Dynamic callback switching
  122. // └── Timing Accuracy
  123. // ├── Millisecond precision verification
  124. // ├── Interval consistency
  125. // └── Execution overhead measurement
  126. //
  127. // IMPORTANCE: These tests validate real-world TaskScheduler usage patterns demonstrating
  128. // practical applications, advanced features, and complex task coordination scenarios
  129. // essential for Arduino and embedded system development.
  130. #include <gtest/gtest.h>
  131. #include "Arduino.h"
  132. // TaskScheduler compile-time feature flags (matching the example)
  133. #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs
  134. #define _TASK_STATUS_REQUEST // Support for StatusRequest functionality
  135. #include "TaskScheduler.h"
  136. // Global test state - simulates LED and tracks execution
  137. std::vector<std::string> blink_test_output;
  138. bool simulated_led_state = false;
  139. int led_state_changes = 0;
  140. bool debug_output_enabled = false;
  141. // Test timing constants (matching the example)
  142. #define PERIOD1 500
  143. #define PERIOD2 400
  144. #define PERIOD3 300
  145. #define PERIOD4 200
  146. #define PERIOD5 600
  147. #define PERIOD6 300
  148. #define DURATION 10000
  149. // Simulated LED control functions
  150. void LEDOn() {
  151. if (!simulated_led_state) {
  152. simulated_led_state = true;
  153. led_state_changes++;
  154. blink_test_output.push_back("LED_ON");
  155. if (debug_output_enabled) {
  156. std::cout << "LED ON at " << millis() << "ms" << std::endl;
  157. }
  158. }
  159. }
  160. void LEDOff() {
  161. if (simulated_led_state) {
  162. simulated_led_state = false;
  163. led_state_changes++;
  164. blink_test_output.push_back("LED_OFF");
  165. if (debug_output_enabled) {
  166. std::cout << "LED OFF at " << millis() << "ms" << std::endl;
  167. }
  168. }
  169. }
  170. // Test callback functions (matching the example structure)
  171. // === APPROACH 1: Simple Flag Driven ===
  172. bool LED_state = false;
  173. void blink1CB();
  174. Scheduler* global_scheduler = nullptr;
  175. Task* tBlink1_ptr = nullptr;
  176. Task* tBlink2_ptr = nullptr;
  177. Task* tBlink3_ptr = nullptr;
  178. Task* tBlink4On_ptr = nullptr;
  179. Task* tBlink4Off_ptr = nullptr;
  180. Task* tBlink5On_ptr = nullptr;
  181. Task* tBlink5Off_ptr = nullptr;
  182. Task* tBlink6_ptr = nullptr;
  183. void blink1CB() {
  184. if (tBlink1_ptr && tBlink1_ptr->isFirstIteration()) {
  185. blink_test_output.push_back("BLINK1_START");
  186. LED_state = false;
  187. }
  188. if (LED_state) {
  189. LEDOff();
  190. LED_state = false;
  191. } else {
  192. LEDOn();
  193. LED_state = true;
  194. }
  195. if (tBlink1_ptr && tBlink1_ptr->isLastIteration()) {
  196. blink_test_output.push_back("BLINK1_END");
  197. LEDOff();
  198. // In real example, would start tBlink2
  199. }
  200. }
  201. // === APPROACH 2: Dual Callback Methods ===
  202. void blink2CB_ON();
  203. void blink2CB_OFF();
  204. void blink2CB_ON() {
  205. if (tBlink2_ptr && tBlink2_ptr->isFirstIteration()) {
  206. blink_test_output.push_back("BLINK2_START");
  207. }
  208. LEDOn();
  209. if (tBlink2_ptr) {
  210. tBlink2_ptr->setCallback(&blink2CB_OFF);
  211. }
  212. if (tBlink2_ptr && tBlink2_ptr->isLastIteration()) {
  213. blink_test_output.push_back("BLINK2_END");
  214. LEDOff();
  215. }
  216. }
  217. void blink2CB_OFF() {
  218. LEDOff();
  219. if (tBlink2_ptr) {
  220. tBlink2_ptr->setCallback(&blink2CB_ON);
  221. }
  222. if (tBlink2_ptr && tBlink2_ptr->isLastIteration()) {
  223. blink_test_output.push_back("BLINK2_END");
  224. LEDOff();
  225. }
  226. }
  227. // === APPROACH 3: Run Counter Driven ===
  228. void blink3CB() {
  229. if (tBlink3_ptr && tBlink3_ptr->isFirstIteration()) {
  230. blink_test_output.push_back("BLINK3_START");
  231. }
  232. if (tBlink3_ptr && (tBlink3_ptr->getRunCounter() & 1)) {
  233. LEDOn();
  234. } else {
  235. LEDOff();
  236. }
  237. if (tBlink3_ptr && tBlink3_ptr->isLastIteration()) {
  238. blink_test_output.push_back("BLINK3_END");
  239. LEDOff();
  240. }
  241. }
  242. // === APPROACH 4: Status Request Based ===
  243. int counter = 0;
  244. bool blink41OE() {
  245. blink_test_output.push_back("BLINK4_START");
  246. counter = 0;
  247. if (tBlink4On_ptr) {
  248. tBlink4On_ptr->setOnEnable(nullptr);
  249. }
  250. return true;
  251. }
  252. void blink41() {
  253. LEDOn();
  254. if (tBlink4On_ptr && tBlink4Off_ptr) {
  255. StatusRequest* r = tBlink4On_ptr->getInternalStatusRequest();
  256. tBlink4Off_ptr->waitForDelayed(r);
  257. }
  258. counter++;
  259. }
  260. void blink42() {
  261. LEDOff();
  262. if (tBlink4On_ptr && tBlink4Off_ptr) {
  263. StatusRequest* r = tBlink4Off_ptr->getInternalStatusRequest();
  264. tBlink4On_ptr->waitForDelayed(r);
  265. }
  266. counter++;
  267. }
  268. void blink42OD() {
  269. if (counter >= DURATION / PERIOD4) {
  270. blink_test_output.push_back("BLINK4_END");
  271. if (tBlink4On_ptr) tBlink4On_ptr->disable();
  272. if (tBlink4Off_ptr) tBlink4Off_ptr->disable();
  273. LEDOff();
  274. }
  275. }
  276. // === APPROACH 5: Interleaving Tasks ===
  277. bool blink51OE() {
  278. blink_test_output.push_back("BLINK5_START");
  279. if (tBlink5On_ptr) {
  280. tBlink5On_ptr->setOnEnable(nullptr);
  281. }
  282. return true;
  283. }
  284. void blink51() {
  285. LEDOn();
  286. }
  287. void blink52() {
  288. LEDOff();
  289. }
  290. void blink52OD() {
  291. blink_test_output.push_back("BLINK5_END");
  292. LEDOff();
  293. }
  294. // === APPROACH 6: Random Interval ===
  295. long interval6 = 0;
  296. bool blink6OE() {
  297. blink_test_output.push_back("BLINK6_START");
  298. interval6 = 500; // Fixed for testing (instead of random)
  299. if (tBlink6_ptr) {
  300. tBlink6_ptr->setInterval(interval6);
  301. }
  302. return true;
  303. }
  304. void blink6CB() {
  305. if (tBlink6_ptr && (tBlink6_ptr->getRunCounter() & 1)) {
  306. LEDOn();
  307. tBlink6_ptr->setInterval(interval6);
  308. } else {
  309. LEDOff();
  310. if (tBlink6_ptr) {
  311. tBlink6_ptr->setInterval(1000 - interval6);
  312. }
  313. }
  314. }
  315. void blink6OD() {
  316. blink_test_output.push_back("BLINK6_END");
  317. LEDOff();
  318. }
  319. // Test helper functions
  320. void clearBlinkTestOutput() {
  321. blink_test_output.clear();
  322. simulated_led_state = false;
  323. led_state_changes = 0;
  324. }
  325. size_t getBlinkTestOutputCount() {
  326. return blink_test_output.size();
  327. }
  328. std::string getBlinkTestOutput(size_t index) {
  329. if (index < blink_test_output.size()) {
  330. return blink_test_output[index];
  331. }
  332. return "";
  333. }
  334. bool runBlinkSchedulerUntil(Scheduler& ts, std::function<bool()> condition, unsigned long timeout_ms = 2000) {
  335. unsigned long start_time = millis();
  336. while (millis() - start_time < timeout_ms) {
  337. bool idle = ts.execute();
  338. if (condition()) {
  339. return true;
  340. }
  341. if (idle) {
  342. delay(1); // Simulate idle sleep
  343. }
  344. }
  345. return false;
  346. }
  347. // Test fixture for blink example validation
  348. class BlinkExampleTest : public ::testing::Test {
  349. protected:
  350. void SetUp() override {
  351. clearBlinkTestOutput();
  352. LED_state = false;
  353. counter = 0;
  354. interval6 = 0;
  355. debug_output_enabled = false;
  356. }
  357. void TearDown() override {
  358. clearBlinkTestOutput();
  359. }
  360. };
  361. // ================== APPROACH 1 TESTS ==================
  362. /**
  363. * @brief Test Approach 1: Simple Flag Driven Blinking
  364. *
  365. * TESTS: Boolean state management, isFirstIteration(), isLastIteration()
  366. *
  367. * PURPOSE: Validate the simplest blinking approach using a boolean flag
  368. * to track LED state. Verifies proper first/last iteration detection,
  369. * state toggling logic, and task lifecycle management.
  370. *
  371. * IMPORTANCE: This is the most fundamental blinking pattern, essential
  372. * for understanding basic TaskScheduler usage and state management.
  373. */
  374. TEST_F(BlinkExampleTest, Approach1_SimpleFlagDriven) {
  375. Scheduler ts;
  376. global_scheduler = &ts;
  377. Task tBlink1(PERIOD1, DURATION / PERIOD1, &blink1CB, &ts, true);
  378. tBlink1_ptr = &tBlink1;
  379. // Should execute first iteration
  380. bool success = runBlinkSchedulerUntil(ts, []() {
  381. return getBlinkTestOutputCount() >= 1;
  382. });
  383. EXPECT_TRUE(success);
  384. EXPECT_EQ(getBlinkTestOutput(0), "BLINK1_START");
  385. EXPECT_TRUE(simulated_led_state); // Should turn LED on first
  386. // Let it run several cycles
  387. success = runBlinkSchedulerUntil(ts, []() {
  388. return led_state_changes >= 6;
  389. }, 3000);
  390. EXPECT_TRUE(success);
  391. EXPECT_GE(led_state_changes, 6);
  392. // Wait for completion
  393. success = runBlinkSchedulerUntil(ts, []() {
  394. return !tBlink1.isEnabled();
  395. }, 15000);
  396. EXPECT_TRUE(success);
  397. EXPECT_FALSE(tBlink1.isEnabled());
  398. // Check that it recorded the end
  399. bool found_end = false;
  400. for (const auto& output : blink_test_output) {
  401. if (output == "BLINK1_END") {
  402. found_end = true;
  403. break;
  404. }
  405. }
  406. EXPECT_TRUE(found_end);
  407. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  408. tBlink1_ptr = nullptr;
  409. }
  410. // ================== APPROACH 2 TESTS ==================
  411. /**
  412. * @brief Test Approach 2: Dual Callback Method Switching
  413. *
  414. * TESTS: setCallback(), dynamic callback switching, dual-method coordination
  415. *
  416. * PURPOSE: Validate the callback switching approach where one callback
  417. * turns LED on and switches to OFF callback, which turns LED off and
  418. * switches back to ON callback, creating a ping-pong pattern.
  419. *
  420. * IMPORTANCE: Demonstrates advanced TaskScheduler feature of runtime
  421. * callback modification, essential for state machine implementations.
  422. */
  423. TEST_F(BlinkExampleTest, Approach2_DualCallbackSwitching) {
  424. Scheduler ts;
  425. Task tBlink2(PERIOD2, DURATION / PERIOD2, &blink2CB_ON, &ts, true);
  426. tBlink2_ptr = &tBlink2;
  427. // Should start with ON callback
  428. bool success = runBlinkSchedulerUntil(ts, []() {
  429. return getBlinkTestOutputCount() >= 1;
  430. });
  431. EXPECT_TRUE(success);
  432. EXPECT_EQ(getBlinkTestOutput(0), "BLINK2_START");
  433. EXPECT_TRUE(simulated_led_state); // First callback turns LED on
  434. // Let it run and switch callbacks several times
  435. success = runBlinkSchedulerUntil(ts, []() {
  436. return led_state_changes >= 8;
  437. }, 4000);
  438. EXPECT_TRUE(success);
  439. EXPECT_GE(led_state_changes, 8);
  440. // Verify alternating pattern
  441. int on_count = 0, off_count = 0;
  442. for (const auto& output : blink_test_output) {
  443. if (output == "LED_ON") on_count++;
  444. if (output == "LED_OFF") off_count++;
  445. }
  446. EXPECT_GT(on_count, 0);
  447. EXPECT_GT(off_count, 0);
  448. EXPECT_LE(abs(on_count - off_count), 1); // Should be roughly equal
  449. // Wait for completion
  450. success = runBlinkSchedulerUntil(ts, []() {
  451. return !tBlink2.isEnabled();
  452. }, 15000);
  453. EXPECT_TRUE(success);
  454. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  455. tBlink2_ptr = nullptr;
  456. }
  457. // ================== APPROACH 3 TESTS ==================
  458. /**
  459. * @brief Test Approach 3: Run Counter Driven Blinking
  460. *
  461. * TESTS: getRunCounter(), odd/even logic, counter-based state control
  462. *
  463. * PURPOSE: Validate blinking controlled by run counter where odd counts
  464. * turn LED on and even counts turn LED off. Tests counter increment
  465. * behavior and bitwise odd/even detection.
  466. *
  467. * IMPORTANCE: Demonstrates practical use of run counters for state
  468. * determination, common in cyclic operations and alternating behaviors.
  469. */
  470. TEST_F(BlinkExampleTest, Approach3_RunCounterDriven) {
  471. Scheduler ts;
  472. Task tBlink3(PERIOD3, DURATION / PERIOD3, &blink3CB, &ts, true);
  473. tBlink3_ptr = &tBlink3;
  474. // Should start execution
  475. bool success = runBlinkSchedulerUntil(ts, []() {
  476. return getBlinkTestOutputCount() >= 1;
  477. });
  478. EXPECT_TRUE(success);
  479. EXPECT_EQ(getBlinkTestOutput(0), "BLINK3_START");
  480. // Let it run several cycles to test counter logic
  481. success = runBlinkSchedulerUntil(ts, []() {
  482. return tBlink3.getRunCounter() >= 6;
  483. }, 3000);
  484. EXPECT_TRUE(success);
  485. EXPECT_GE(tBlink3.getRunCounter(), 6);
  486. // Verify that LED state follows counter parity
  487. // Run counter starts at 1 (odd) = LED ON, 2 (even) = LED OFF, etc.
  488. int run_count = tBlink3.getRunCounter();
  489. if (run_count & 1) {
  490. EXPECT_TRUE(simulated_led_state);
  491. } else {
  492. EXPECT_FALSE(simulated_led_state);
  493. }
  494. // Wait for completion
  495. success = runBlinkSchedulerUntil(ts, []() {
  496. return !tBlink3.isEnabled();
  497. }, 15000);
  498. EXPECT_TRUE(success);
  499. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  500. tBlink3_ptr = nullptr;
  501. }
  502. // ================== APPROACH 4 TESTS ==================
  503. /**
  504. * @brief Test Approach 4: Status Request Coordination
  505. *
  506. * TESTS: getInternalStatusRequest(), waitForDelayed(), OnEnable/OnDisable callbacks
  507. *
  508. * PURPOSE: Validate the most advanced blinking approach using status
  509. * requests for inter-task coordination. Tests two tasks passing control
  510. * back and forth using internal status request objects.
  511. *
  512. * IMPORTANCE: Demonstrates sophisticated task coordination patterns
  513. * essential for complex embedded systems requiring precise timing
  514. * and event-driven behaviors.
  515. */
  516. TEST_F(BlinkExampleTest, Approach4_StatusRequestCoordination) {
  517. Scheduler ts;
  518. Task tBlink4On(PERIOD4, TASK_ONCE, &blink41, &ts, false, &blink41OE);
  519. Task tBlink4Off(PERIOD4, TASK_ONCE, &blink42, &ts, false, nullptr, &blink42OD);
  520. tBlink4On_ptr = &tBlink4On;
  521. tBlink4Off_ptr = &tBlink4Off;
  522. // Enable the first task to start the sequence
  523. tBlink4On.enable();
  524. // Should execute OnEnable and start
  525. bool success = runBlinkSchedulerUntil(ts, []() {
  526. return getBlinkTestOutputCount() >= 1;
  527. });
  528. EXPECT_TRUE(success);
  529. EXPECT_EQ(getBlinkTestOutput(0), "BLINK4_START");
  530. // Let the ping-pong pattern run
  531. success = runBlinkSchedulerUntil(ts, []() {
  532. return counter >= 10;
  533. }, 3000);
  534. EXPECT_TRUE(success);
  535. EXPECT_GE(counter, 10);
  536. // Verify both tasks are coordinating (alternating LED states)
  537. EXPECT_GE(led_state_changes, 5);
  538. // Wait for the sequence to complete
  539. success = runBlinkSchedulerUntil(ts, []() {
  540. return counter >= DURATION / PERIOD4;
  541. }, 15000);
  542. EXPECT_TRUE(success);
  543. // Both tasks should be disabled by OnDisable callback
  544. EXPECT_FALSE(tBlink4On.isEnabled());
  545. EXPECT_FALSE(tBlink4Off.isEnabled());
  546. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  547. tBlink4On_ptr = nullptr;
  548. tBlink4Off_ptr = nullptr;
  549. }
  550. // ================== APPROACH 5 TESTS ==================
  551. /**
  552. * @brief Test Approach 5: Interleaving Tasks
  553. *
  554. * TESTS: Phase-shifted dual tasks, OnEnable callback management, synchronized execution
  555. *
  556. * PURPOSE: Validate two independent tasks running with phase offset
  557. * where one task turns LED on and another turns it off, creating
  558. * precise timing control through task interleaving.
  559. *
  560. * IMPORTANCE: Demonstrates advanced timing patterns for applications
  561. * requiring precise phase relationships and independent task control.
  562. */
  563. TEST_F(BlinkExampleTest, Approach5_InterleavingTasks) {
  564. Scheduler ts;
  565. Task tBlink5On(PERIOD5, DURATION / PERIOD5, &blink51, &ts, false, &blink51OE);
  566. Task tBlink5Off(PERIOD5, DURATION / PERIOD5, &blink52, &ts, false, nullptr, &blink52OD);
  567. tBlink5On_ptr = &tBlink5On;
  568. tBlink5Off_ptr = &tBlink5Off;
  569. // Start both tasks (in real example, they'd be phase-shifted)
  570. tBlink5On.enable();
  571. tBlink5Off.enable();
  572. // Should execute OnEnable
  573. bool success = runBlinkSchedulerUntil(ts, []() {
  574. return getBlinkTestOutputCount() >= 1;
  575. });
  576. EXPECT_TRUE(success);
  577. EXPECT_EQ(getBlinkTestOutput(0), "BLINK5_START");
  578. // Let both tasks run
  579. success = runBlinkSchedulerUntil(ts, []() {
  580. return led_state_changes >= 8;
  581. }, 4000);
  582. EXPECT_TRUE(success);
  583. EXPECT_GE(led_state_changes, 8);
  584. // Both tasks should be enabled and running
  585. EXPECT_TRUE(tBlink5On.isEnabled() || tBlink5Off.isEnabled());
  586. // Wait for completion
  587. success = runBlinkSchedulerUntil(ts, []() {
  588. return !tBlink5On.isEnabled() && !tBlink5Off.isEnabled();
  589. }, 15000);
  590. EXPECT_TRUE(success);
  591. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  592. tBlink5On_ptr = nullptr;
  593. tBlink5Off_ptr = nullptr;
  594. }
  595. // ================== APPROACH 6 TESTS ==================
  596. /**
  597. * @brief Test Approach 6: Random Interval Generation
  598. *
  599. * TESTS: setInterval(), dynamic interval modification, run counter + interval combination
  600. *
  601. * PURPOSE: Validate dynamic interval adjustment where LED ON time is
  602. * random and OFF time is complementary to maintain constant period.
  603. * Tests runtime interval modification and complex timing patterns.
  604. *
  605. * IMPORTANCE: Demonstrates adaptive timing behaviors essential for
  606. * applications requiring variable timing patterns while maintaining
  607. * overall system timing constraints.
  608. */
  609. TEST_F(BlinkExampleTest, Approach6_RandomIntervalGeneration) {
  610. Scheduler ts;
  611. Task tBlink6(PERIOD6, DURATION / PERIOD6, &blink6CB, &ts, false, &blink6OE, &blink6OD);
  612. tBlink6_ptr = &tBlink6;
  613. // Enable task to start
  614. tBlink6.enable();
  615. // Should execute OnEnable and set initial interval
  616. bool success = runBlinkSchedulerUntil(ts, []() {
  617. return getBlinkTestOutputCount() >= 1;
  618. });
  619. EXPECT_TRUE(success);
  620. EXPECT_EQ(getBlinkTestOutput(0), "BLINK6_START");
  621. EXPECT_EQ(interval6, 500); // Fixed value for testing
  622. EXPECT_EQ(tBlink6.getInterval(), 500);
  623. // Let it run and change intervals
  624. success = runBlinkSchedulerUntil(ts, []() {
  625. return tBlink6.getRunCounter() >= 4;
  626. }, 3000);
  627. EXPECT_TRUE(success);
  628. EXPECT_GE(tBlink6.getRunCounter(), 4);
  629. // Verify interval changes based on run counter
  630. if (tBlink6.getRunCounter() & 1) {
  631. EXPECT_EQ(tBlink6.getInterval(), interval6); // ON interval
  632. EXPECT_TRUE(simulated_led_state);
  633. } else {
  634. EXPECT_EQ(tBlink6.getInterval(), 1000 - interval6); // OFF interval
  635. EXPECT_FALSE(simulated_led_state);
  636. }
  637. // Wait for completion
  638. success = runBlinkSchedulerUntil(ts, []() {
  639. return !tBlink6.isEnabled();
  640. }, 15000);
  641. EXPECT_TRUE(success);
  642. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  643. tBlink6_ptr = nullptr;
  644. }
  645. // ================== INTEGRATION TESTS ==================
  646. /**
  647. * @brief Test Sequential Task Chain Execution
  648. *
  649. * TESTS: Complete blink example sequence, task chaining, scheduler coordination
  650. *
  651. * PURPOSE: Validate the complete blink example workflow where each
  652. * approach runs for its duration then triggers the next approach,
  653. * creating a continuous demonstration cycle.
  654. *
  655. * IMPORTANCE: Verifies end-to-end scheduler behavior with complex
  656. * task relationships, timing coordination, and state management
  657. * across multiple interconnected blinking patterns.
  658. */
  659. TEST_F(BlinkExampleTest, SequentialTaskChainExecution) {
  660. Scheduler ts;
  661. debug_output_enabled = true;
  662. // Create all tasks (simplified version)
  663. Task tBlink1(PERIOD1, 4, &blink1CB, &ts, true); // Shorter duration for testing
  664. tBlink1_ptr = &tBlink1;
  665. // Run just the first approach for integration test
  666. bool success = runBlinkSchedulerUntil(ts, []() {
  667. return getBlinkTestOutputCount() >= 1;
  668. });
  669. EXPECT_TRUE(success);
  670. EXPECT_EQ(getBlinkTestOutput(0), "BLINK1_START");
  671. // Let it complete its cycles
  672. success = runBlinkSchedulerUntil(ts, []() {
  673. return !tBlink1.isEnabled();
  674. }, 5000);
  675. EXPECT_TRUE(success);
  676. EXPECT_FALSE(tBlink1.isEnabled());
  677. // Verify proper execution pattern
  678. EXPECT_GE(led_state_changes, 4); // Should have blinked at least twice
  679. EXPECT_FALSE(simulated_led_state); // Should end with LED off
  680. tBlink1_ptr = nullptr;
  681. debug_output_enabled = false;
  682. }
  683. /**
  684. * @brief Test Scheduler Core Functionality with Blink Patterns
  685. *
  686. * TESTS: execute() return values, task management, timing accuracy
  687. *
  688. * PURPOSE: Validate scheduler core behavior under the complex timing
  689. * and callback patterns used in the blink example. Tests idle detection,
  690. * execution ordering, and overall scheduler robustness.
  691. *
  692. * IMPORTANCE: Ensures the scheduler performs correctly under realistic
  693. * workloads with multiple timing patterns, callback switches, and
  694. * complex task interdependencies typical of real applications.
  695. */
  696. TEST_F(BlinkExampleTest, SchedulerCoreFunctionalityValidation) {
  697. Scheduler ts;
  698. // Create a simple blinking task
  699. Task tBlink(500, 6, &blink1CB, &ts, true);
  700. tBlink1_ptr = &tBlink;
  701. int execute_calls = 0;
  702. int idle_returns = 0;
  703. // Monitor scheduler execution behavior
  704. unsigned long start_time = millis();
  705. while (millis() - start_time < 4000 && tBlink.isEnabled()) {
  706. bool idle = ts.execute();
  707. execute_calls++;
  708. if (idle) {
  709. idle_returns++;
  710. }
  711. }
  712. // Verify scheduler execution statistics
  713. EXPECT_GT(execute_calls, 100); // Should have been called many times
  714. EXPECT_GT(idle_returns, 50); // Should have had idle periods
  715. EXPECT_LE(idle_returns, execute_calls); // Idle returns <= total calls
  716. // Verify task completed successfully
  717. EXPECT_FALSE(tBlink.isEnabled());
  718. EXPECT_GE(led_state_changes, 6); // Should have blinked 3 times (on/off cycles)
  719. tBlink1_ptr = nullptr;
  720. }
  721. /**
  722. * @brief Main test runner function for blink example tests
  723. */
  724. int main(int argc, char **argv) {
  725. ::testing::InitGoogleTest(&argc, argv);
  726. return RUN_ALL_TESTS();
  727. }