test-scheduler-blink-example.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  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. // Test condition functions to replace lambda functions
  335. bool condition_output_count_1() {
  336. return getBlinkTestOutputCount() >= 1;
  337. }
  338. bool condition_led_changes_6() {
  339. return led_state_changes >= 6;
  340. }
  341. bool condition_led_changes_8() {
  342. return led_state_changes >= 8;
  343. }
  344. bool condition_led_changes_5() {
  345. return led_state_changes >= 5;
  346. }
  347. bool condition_led_changes_4() {
  348. return led_state_changes >= 4;
  349. }
  350. bool condition_tBlink1_disabled() {
  351. return tBlink1_ptr && !tBlink1_ptr->isEnabled();
  352. }
  353. bool condition_tBlink2_disabled() {
  354. return tBlink2_ptr && !tBlink2_ptr->isEnabled();
  355. }
  356. bool condition_tBlink3_disabled() {
  357. return tBlink3_ptr && !tBlink3_ptr->isEnabled();
  358. }
  359. bool condition_tBlink3_runcount_6() {
  360. return tBlink3_ptr && tBlink3_ptr->getRunCounter() >= 6;
  361. }
  362. bool condition_counter_10() {
  363. return counter >= 10;
  364. }
  365. bool condition_counter_duration_period4() {
  366. return counter >= DURATION / PERIOD4;
  367. }
  368. bool condition_both_blink4_disabled() {
  369. return tBlink4On_ptr && tBlink4Off_ptr &&
  370. !tBlink4On_ptr->isEnabled() && !tBlink4Off_ptr->isEnabled();
  371. }
  372. bool condition_both_blink5_disabled() {
  373. return tBlink5On_ptr && tBlink5Off_ptr &&
  374. !tBlink5On_ptr->isEnabled() && !tBlink5Off_ptr->isEnabled();
  375. }
  376. bool condition_tBlink6_disabled() {
  377. return tBlink6_ptr && !tBlink6_ptr->isEnabled();
  378. }
  379. bool condition_tBlink6_runcount_4() {
  380. return tBlink6_ptr && tBlink6_ptr->getRunCounter() >= 4;
  381. }
  382. // Scheduler execution helper with callback function pointer
  383. bool runBlinkSchedulerUntil(Scheduler& ts, bool (*condition)(), unsigned long timeout_ms = 2000) {
  384. unsigned long start_time = millis();
  385. while (millis() - start_time < timeout_ms) {
  386. bool idle = ts.execute();
  387. if (condition()) {
  388. return true;
  389. }
  390. if (idle) {
  391. delay(1); // Simulate idle sleep
  392. }
  393. }
  394. return false;
  395. }
  396. // Test fixture for blink example validation
  397. class BlinkExampleTest : public ::testing::Test {
  398. protected:
  399. void SetUp() override {
  400. clearBlinkTestOutput();
  401. LED_state = false;
  402. counter = 0;
  403. interval6 = 0;
  404. debug_output_enabled = false;
  405. }
  406. void TearDown() override {
  407. clearBlinkTestOutput();
  408. }
  409. };
  410. // ================== APPROACH 1 TESTS ==================
  411. /**
  412. * @brief Test Approach 1: Simple Flag Driven Blinking
  413. *
  414. * TESTS: Boolean state management, isFirstIteration(), isLastIteration()
  415. *
  416. * PURPOSE: Validate the simplest blinking approach using a boolean flag
  417. * to track LED state. Verifies proper first/last iteration detection,
  418. * state toggling logic, and task lifecycle management.
  419. *
  420. * IMPORTANCE: This is the most fundamental blinking pattern, essential
  421. * for understanding basic TaskScheduler usage and state management.
  422. */
  423. TEST_F(BlinkExampleTest, Approach1_SimpleFlagDriven) {
  424. Scheduler ts;
  425. global_scheduler = &ts;
  426. Task tBlink1(PERIOD1, DURATION / PERIOD1, &blink1CB, &ts, true);
  427. tBlink1_ptr = &tBlink1;
  428. // Should execute first iteration
  429. bool success = runBlinkSchedulerUntil(ts, condition_output_count_1);
  430. EXPECT_TRUE(success);
  431. EXPECT_EQ(getBlinkTestOutput(0), "BLINK1_START");
  432. EXPECT_TRUE(simulated_led_state); // Should turn LED on first
  433. // Let it run several cycles
  434. success = runBlinkSchedulerUntil(ts, condition_led_changes_6, 3000);
  435. EXPECT_TRUE(success);
  436. EXPECT_GE(led_state_changes, 6);
  437. // Wait for completion
  438. success = runBlinkSchedulerUntil(ts, condition_tBlink1_disabled, 15000);
  439. EXPECT_TRUE(success);
  440. EXPECT_FALSE(tBlink1.isEnabled());
  441. // Check that it recorded the end
  442. bool found_end = false;
  443. for (const auto& output : blink_test_output) {
  444. if (output == "BLINK1_END") {
  445. found_end = true;
  446. break;
  447. }
  448. }
  449. EXPECT_TRUE(found_end);
  450. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  451. tBlink1_ptr = nullptr;
  452. }
  453. // ================== APPROACH 2 TESTS ==================
  454. /**
  455. * @brief Test Approach 2: Dual Callback Method Switching
  456. *
  457. * TESTS: setCallback(), dynamic callback switching, dual-method coordination
  458. *
  459. * PURPOSE: Validate the callback switching approach where one callback
  460. * turns LED on and switches to OFF callback, which turns LED off and
  461. * switches back to ON callback, creating a ping-pong pattern.
  462. *
  463. * IMPORTANCE: Demonstrates advanced TaskScheduler feature of runtime
  464. * callback modification, essential for state machine implementations.
  465. */
  466. TEST_F(BlinkExampleTest, Approach2_DualCallbackSwitching) {
  467. Scheduler ts;
  468. Task tBlink2(PERIOD2, DURATION / PERIOD2, &blink2CB_ON, &ts, true);
  469. tBlink2_ptr = &tBlink2;
  470. // Should start with ON callback
  471. bool success = runBlinkSchedulerUntil(ts, condition_output_count_1);
  472. EXPECT_TRUE(success);
  473. EXPECT_EQ(getBlinkTestOutput(0), "BLINK2_START");
  474. EXPECT_TRUE(simulated_led_state); // First callback turns LED on
  475. // Let it run and switch callbacks several times
  476. success = runBlinkSchedulerUntil(ts, condition_led_changes_8, 4000);
  477. EXPECT_TRUE(success);
  478. EXPECT_GE(led_state_changes, 8);
  479. // Verify alternating pattern
  480. int on_count = 0, off_count = 0;
  481. for (const auto& output : blink_test_output) {
  482. if (output == "LED_ON") on_count++;
  483. if (output == "LED_OFF") off_count++;
  484. }
  485. EXPECT_GT(on_count, 0);
  486. EXPECT_GT(off_count, 0);
  487. EXPECT_LE(abs(on_count - off_count), 1); // Should be roughly equal
  488. // Wait for completion
  489. success = runBlinkSchedulerUntil(ts, condition_tBlink2_disabled, 15000);
  490. EXPECT_TRUE(success);
  491. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  492. tBlink2_ptr = nullptr;
  493. }
  494. // ================== APPROACH 3 TESTS ==================
  495. /**
  496. * @brief Test Approach 3: Run Counter Driven Blinking
  497. *
  498. * TESTS: getRunCounter(), odd/even logic, counter-based state control
  499. *
  500. * PURPOSE: Validate blinking controlled by run counter where odd counts
  501. * turn LED on and even counts turn LED off. Tests counter increment
  502. * behavior and bitwise odd/even detection.
  503. *
  504. * IMPORTANCE: Demonstrates practical use of run counters for state
  505. * determination, common in cyclic operations and alternating behaviors.
  506. */
  507. TEST_F(BlinkExampleTest, Approach3_RunCounterDriven) {
  508. Scheduler ts;
  509. Task tBlink3(PERIOD3, DURATION / PERIOD3, &blink3CB, &ts, true);
  510. tBlink3_ptr = &tBlink3;
  511. // Should start execution
  512. bool success = runBlinkSchedulerUntil(ts, condition_output_count_1);
  513. EXPECT_TRUE(success);
  514. EXPECT_EQ(getBlinkTestOutput(0), "BLINK3_START");
  515. // Let it run several cycles to test counter logic
  516. success = runBlinkSchedulerUntil(ts, condition_tBlink3_runcount_6, 3000);
  517. EXPECT_TRUE(success);
  518. EXPECT_GE(tBlink3.getRunCounter(), 6);
  519. // Verify that LED state follows counter parity
  520. // Run counter starts at 1 (odd) = LED ON, 2 (even) = LED OFF, etc.
  521. int run_count = tBlink3.getRunCounter();
  522. if (run_count & 1) {
  523. EXPECT_TRUE(simulated_led_state);
  524. } else {
  525. EXPECT_FALSE(simulated_led_state);
  526. }
  527. // Wait for completion
  528. success = runBlinkSchedulerUntil(ts, condition_tBlink3_disabled, 15000);
  529. EXPECT_TRUE(success);
  530. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  531. tBlink3_ptr = nullptr;
  532. }
  533. // ================== APPROACH 4 TESTS ==================
  534. /**
  535. * @brief Test Approach 4: Status Request Coordination
  536. *
  537. * TESTS: getInternalStatusRequest(), waitForDelayed(), OnEnable/OnDisable callbacks
  538. *
  539. * PURPOSE: Validate the most advanced blinking approach using status
  540. * requests for inter-task coordination. Tests two tasks passing control
  541. * back and forth using internal status request objects.
  542. *
  543. * IMPORTANCE: Demonstrates sophisticated task coordination patterns
  544. * essential for complex embedded systems requiring precise timing
  545. * and event-driven behaviors.
  546. */
  547. TEST_F(BlinkExampleTest, Approach4_StatusRequestCoordination) {
  548. Scheduler ts;
  549. Task tBlink4On(PERIOD4, TASK_ONCE, &blink41, &ts, false, &blink41OE);
  550. Task tBlink4Off(PERIOD4, TASK_ONCE, &blink42, &ts, false, nullptr, &blink42OD);
  551. tBlink4On_ptr = &tBlink4On;
  552. tBlink4Off_ptr = &tBlink4Off;
  553. // Enable the first task to start the sequence
  554. tBlink4On.enable();
  555. // Should execute OnEnable and start
  556. bool success = runBlinkSchedulerUntil(ts, condition_output_count_1);
  557. EXPECT_TRUE(success);
  558. EXPECT_EQ(getBlinkTestOutput(0), "BLINK4_START");
  559. // Let the ping-pong pattern run
  560. success = runBlinkSchedulerUntil(ts, condition_counter_10, 3000);
  561. EXPECT_TRUE(success);
  562. EXPECT_GE(counter, 10);
  563. // Verify both tasks are coordinating (alternating LED states)
  564. EXPECT_GE(led_state_changes, 5);
  565. // Wait for the sequence to complete
  566. success = runBlinkSchedulerUntil(ts, condition_counter_duration_period4, 15000);
  567. EXPECT_TRUE(success);
  568. ts.execute(); // Final execute to process disable
  569. // Both tasks should be disabled by OnDisable callback
  570. EXPECT_FALSE(tBlink4On.isEnabled());
  571. EXPECT_FALSE(tBlink4Off.isEnabled());
  572. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  573. tBlink4On_ptr = nullptr;
  574. tBlink4Off_ptr = nullptr;
  575. }
  576. // ================== APPROACH 5 TESTS ==================
  577. /**
  578. * @brief Test Approach 5: Interleaving Tasks
  579. *
  580. * TESTS: Phase-shifted dual tasks, OnEnable callback management, synchronized execution
  581. *
  582. * PURPOSE: Validate two independent tasks running with phase offset
  583. * where one task turns LED on and another turns it off, creating
  584. * precise timing control through task interleaving.
  585. *
  586. * IMPORTANCE: Demonstrates advanced timing patterns for applications
  587. * requiring precise phase relationships and independent task control.
  588. */
  589. TEST_F(BlinkExampleTest, Approach5_InterleavingTasks) {
  590. Scheduler ts;
  591. Task tBlink5On(PERIOD5, DURATION / PERIOD5, &blink51, &ts, false, &blink51OE);
  592. Task tBlink5Off(PERIOD5, DURATION / PERIOD5, &blink52, &ts, false, nullptr, &blink52OD);
  593. tBlink5On_ptr = &tBlink5On;
  594. tBlink5Off_ptr = &tBlink5Off;
  595. // Start both tasks (in real example, they'd be phase-shifted)
  596. tBlink5On.enable();
  597. tBlink5Off.enable();
  598. // Should execute OnEnable
  599. bool success = runBlinkSchedulerUntil(ts, condition_output_count_1);
  600. EXPECT_TRUE(success);
  601. EXPECT_EQ(getBlinkTestOutput(0), "BLINK5_START");
  602. // Let both tasks run
  603. success = runBlinkSchedulerUntil(ts, condition_led_changes_8, 4000);
  604. EXPECT_TRUE(success);
  605. EXPECT_GE(led_state_changes, 8);
  606. // Both tasks should be enabled and running
  607. EXPECT_TRUE(tBlink5On.isEnabled() || tBlink5Off.isEnabled());
  608. // Wait for completion
  609. success = runBlinkSchedulerUntil(ts, condition_both_blink5_disabled, 15000);
  610. EXPECT_TRUE(success);
  611. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  612. tBlink5On_ptr = nullptr;
  613. tBlink5Off_ptr = nullptr;
  614. }
  615. // ================== APPROACH 6 TESTS ==================
  616. /**
  617. * @brief Test Approach 6: Random Interval Generation
  618. *
  619. * TESTS: setInterval(), dynamic interval modification, run counter + interval combination
  620. *
  621. * PURPOSE: Validate dynamic interval adjustment where LED ON time is
  622. * random and OFF time is complementary to maintain constant period.
  623. * Tests runtime interval modification and complex timing patterns.
  624. *
  625. * IMPORTANCE: Demonstrates adaptive timing behaviors essential for
  626. * applications requiring variable timing patterns while maintaining
  627. * overall system timing constraints.
  628. */
  629. TEST_F(BlinkExampleTest, Approach6_RandomIntervalGeneration) {
  630. Scheduler ts;
  631. Task tBlink6(PERIOD6, DURATION / PERIOD6, &blink6CB, &ts, false, &blink6OE, &blink6OD);
  632. tBlink6_ptr = &tBlink6;
  633. // Enable task to start
  634. tBlink6.enable();
  635. // Should execute OnEnable and set initial interval
  636. bool success = runBlinkSchedulerUntil(ts, condition_output_count_1);
  637. EXPECT_TRUE(success);
  638. EXPECT_EQ(getBlinkTestOutput(0), "BLINK6_START");
  639. EXPECT_EQ(interval6, 500); // Fixed value for testing
  640. EXPECT_EQ(tBlink6.getInterval(), 500);
  641. // Let it run and change intervals
  642. success = runBlinkSchedulerUntil(ts, condition_tBlink6_runcount_4, 3000);
  643. EXPECT_TRUE(success);
  644. EXPECT_GE(tBlink6.getRunCounter(), 4);
  645. // Verify interval changes based on run counter
  646. if (tBlink6.getRunCounter() & 1) {
  647. EXPECT_EQ(tBlink6.getInterval(), interval6); // ON interval
  648. EXPECT_TRUE(simulated_led_state);
  649. } else {
  650. EXPECT_EQ(tBlink6.getInterval(), 1000 - interval6); // OFF interval
  651. EXPECT_FALSE(simulated_led_state);
  652. }
  653. // Wait for completion
  654. success = runBlinkSchedulerUntil(ts, condition_tBlink6_disabled, 15000);
  655. EXPECT_TRUE(success);
  656. EXPECT_FALSE(simulated_led_state); // LED should be off at end
  657. tBlink6_ptr = nullptr;
  658. }
  659. // ================== INTEGRATION TESTS ==================
  660. /**
  661. * @brief Test Sequential Task Chain Execution
  662. *
  663. * TESTS: Complete blink example sequence, task chaining, scheduler coordination
  664. *
  665. * PURPOSE: Validate the complete blink example workflow where each
  666. * approach runs for its duration then triggers the next approach,
  667. * creating a continuous demonstration cycle.
  668. *
  669. * IMPORTANCE: Verifies end-to-end scheduler behavior with complex
  670. * task relationships, timing coordination, and state management
  671. * across multiple interconnected blinking patterns.
  672. */
  673. TEST_F(BlinkExampleTest, SequentialTaskChainExecution) {
  674. Scheduler ts;
  675. debug_output_enabled = true;
  676. // Create all tasks (simplified version)
  677. Task tBlink1(PERIOD1, 4, &blink1CB, &ts, true); // Shorter duration for testing
  678. tBlink1_ptr = &tBlink1;
  679. // Run just the first approach for integration test
  680. bool success = runBlinkSchedulerUntil(ts, condition_output_count_1);
  681. EXPECT_TRUE(success);
  682. EXPECT_EQ(getBlinkTestOutput(0), "BLINK1_START");
  683. // Let it complete its cycles
  684. success = runBlinkSchedulerUntil(ts, condition_tBlink1_disabled, 5000);
  685. EXPECT_TRUE(success);
  686. EXPECT_FALSE(tBlink1.isEnabled());
  687. // Verify proper execution pattern
  688. EXPECT_GE(led_state_changes, 4); // Should have blinked at least twice
  689. EXPECT_FALSE(simulated_led_state); // Should end with LED off
  690. tBlink1_ptr = nullptr;
  691. debug_output_enabled = false;
  692. }
  693. /**
  694. * @brief Test Scheduler Core Functionality with Blink Patterns
  695. *
  696. * TESTS: execute() return values, task management, timing accuracy
  697. *
  698. * PURPOSE: Validate scheduler core behavior under the complex timing
  699. * and callback patterns used in the blink example. Tests idle detection,
  700. * execution ordering, and overall scheduler robustness.
  701. *
  702. * IMPORTANCE: Ensures the scheduler performs correctly under realistic
  703. * workloads with multiple timing patterns, callback switches, and
  704. * complex task interdependencies typical of real applications.
  705. */
  706. TEST_F(BlinkExampleTest, SchedulerCoreFunctionalityValidation) {
  707. Scheduler ts;
  708. // Create a simple blinking task
  709. Task tBlink(500, 6, &blink1CB, &ts, true);
  710. tBlink1_ptr = &tBlink;
  711. int execute_calls = 0;
  712. int idle_returns = 0;
  713. // Monitor scheduler execution behavior
  714. unsigned long start_time = millis();
  715. while (millis() - start_time < 4000 && tBlink.isEnabled()) {
  716. bool idle = ts.execute();
  717. execute_calls++;
  718. if (idle) {
  719. idle_returns++;
  720. }
  721. }
  722. // Verify scheduler execution statistics
  723. EXPECT_GT(execute_calls, 100); // Should have been called many times
  724. EXPECT_GT(idle_returns, 50); // Should have had idle periods
  725. EXPECT_LE(idle_returns, execute_calls); // Idle returns <= total calls
  726. // Verify task completed successfully
  727. EXPECT_FALSE(tBlink.isEnabled());
  728. EXPECT_GE(led_state_changes, 6); // Should have blinked 3 times (on/off cycles)
  729. tBlink1_ptr = nullptr;
  730. }
  731. /**
  732. * @brief Main test runner function for blink example tests
  733. */
  734. int main(int argc, char **argv) {
  735. ::testing::InitGoogleTest(&argc, argv);
  736. return RUN_ALL_TESTS();
  737. }