test-scheduler-basic-thorough.cpp 71 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018
  1. // test-scheduler-basic-thorough-no-lambda.cpp - Comprehensive TaskScheduler unit tests without lambda functions
  2. // This file contains comprehensive tests for basic TaskScheduler functionality
  3. // without using lambda functions, ensuring compatibility with all Arduino platforms
  4. //
  5. // =====================================================================================
  6. // COMPREHENSIVE TASKSCHEDULER TEST PLAN AND COVERAGE MATRIX
  7. // =====================================================================================
  8. //
  9. // PURPOSE: Validate all basic TaskScheduler methods and patterns without compile options
  10. // APPROACH: Traditional function pointers and callback functions for maximum compatibility
  11. // SCOPE: Core functionality excluding advanced features requiring compile-time directives
  12. //
  13. // COVERAGE MATRIX:
  14. // ================
  15. //
  16. // 1. TASK CONSTRUCTOR TESTS
  17. // ├── Default Constructor
  18. // │ └── Task() - creates inert task with safe defaults
  19. // ├── Parameterized Constructor
  20. // │ └── Task(interval, iterations, callback, scheduler, enable) - full configuration
  21. // ├── Lifecycle Constructor
  22. // │ └── Task(..., onEnable, onDisable) - with lifecycle callbacks
  23. // └── Auto-enabled Constructor
  24. // └── Task(..., enable=true) - immediate activation patterns
  25. //
  26. // 2. TASK INFORMATION METHODS
  27. // ├── State Queries
  28. // │ ├── isEnabled() - current activation state
  29. // │ ├── getInterval() - execution timing interval
  30. // │ ├── getIterations() - remaining execution count
  31. // │ └── getRunCounter() - total executions completed
  32. // └── Lifecycle Tracking
  33. // └── Information accuracy across task lifecycle stages
  34. //
  35. // 3. TASK CONTROL METHODS
  36. // ├── Basic Control
  37. // │ ├── enable() - activate task for execution
  38. // │ ├── disable() - deactivate task execution
  39. // │ └── enableIfNot() - conditional enabling
  40. // ├── Restart Operations
  41. // │ ├── restart() - reset iteration count and re-enable
  42. // │ └── restartDelayed(delay) - restart with initial delay
  43. // └── State Management
  44. // └── Control method interactions and state consistency
  45. //
  46. // 4. TASK TIMING METHODS
  47. // ├── Execution Control
  48. // │ ├── delay(ms) - postpone next execution
  49. // │ ├── forceNextIteration() - immediate execution trigger
  50. // │ └── enableDelayed(delay) - delayed activation
  51. // └── Timing Behavior
  52. // └── Interval and delay interactions with scheduler
  53. //
  54. // 5. TASK CONFIGURATION METHODS
  55. // ├── Parameter Configuration
  56. // │ ├── set(interval, iterations, callback, onEnable, onDisable) - complete reconfiguration
  57. // │ ├── setInterval(interval) - dynamic timing changes
  58. // │ └── setIterations(iterations) - dynamic repeat count
  59. // └── Callback Management
  60. // ├── setCallback(callback) - change execution function
  61. // ├── setOnEnable(callback) - change enable callback
  62. // └── setOnDisable(callback) - change disable callback
  63. //
  64. // 6. TASK ITERATION STATE METHODS
  65. // ├── Execution Context
  66. // │ ├── isFirstIteration() - detect initial execution
  67. // │ └── isLastIteration() - detect final execution
  68. // └── Context-Aware Programming
  69. // └── Conditional logic based on iteration position
  70. //
  71. // 7. TASK CALLBACK SWITCHING METHODS
  72. // ├── State Machine Support
  73. // │ ├── yield(callback) - permanent callback change
  74. // │ └── yieldOnce(callback) - single execution callback
  75. // └── Multi-Phase Processing
  76. // └── Sequential callback execution patterns
  77. //
  78. // 8. TASK LIFECYCLE CALLBACKS
  79. // ├── Enable Events
  80. // │ ├── onEnable() callbacks - activation event handling
  81. // │ └── Conditional enabling via return values
  82. // └── Disable Events
  83. // └── onDisable() callbacks - deactivation event handling
  84. //
  85. // 9. SCHEDULER CONSTRUCTOR AND MANAGEMENT
  86. // ├── Basic Operations
  87. // │ ├── Scheduler() - empty scheduler creation
  88. // │ ├── execute() - task execution cycle
  89. // │ └── init() - scheduler reinitialization
  90. // └── Robustness
  91. // └── Safe operation with no tasks or empty states
  92. //
  93. // 10. SCHEDULER TASK MANAGEMENT
  94. // ├── Manual Task Control
  95. // │ ├── addTask(task) - explicit task registration
  96. // │ └── deleteTask(task) - explicit task removal
  97. // └── Dynamic Management
  98. // └── Runtime task addition and removal patterns
  99. //
  100. // 11. SCHEDULER EXECUTION CONTROL
  101. // ├── Execution Monitoring
  102. // │ ├── execute() return values - idle detection
  103. // │ ├── enableAll() - bulk task activation
  104. // │ └── disableAll() - bulk task deactivation
  105. // └── System Control
  106. // └── Coordinated task state management
  107. //
  108. // 12. SCHEDULER TIME QUERIES
  109. // ├── Timing Information
  110. // │ └── timeUntilNextIteration(task) - predictive timing
  111. // └── Scheduling Coordination
  112. // └── Task timing coordination and synchronization
  113. //
  114. // 13. SCHEDULER TASK ACCESS
  115. // ├── Context Information
  116. // │ └── getCurrentTask() - callback context identification
  117. // └── Self-Reference Patterns
  118. // └── Task self-modification and introspection
  119. //
  120. // 14. SCHEDULER TIMING CONTROL
  121. // ├── Global Control
  122. // │ └── startNow() - immediate execution trigger
  123. // └── Synchronized Operations
  124. // └── Coordinated task restart and execution
  125. //
  126. // 15. INTEGRATION TESTS
  127. // ├── Complete Lifecycle
  128. // │ └── Full task lifecycle with all features
  129. // └── Multi-Task Coordination
  130. // └── Complex scenarios with multiple concurrent tasks
  131. //
  132. // 16. EDGE CASES AND ERROR HANDLING
  133. // ├── Boundary Conditions
  134. // │ ├── Zero iterations - invalid execution counts
  135. // │ ├── Infinite iterations - TASK_FOREVER handling
  136. // │ └── Null callbacks - robustness testing
  137. // └── Defensive Programming
  138. // └── Safe handling of invalid inputs and states
  139. //
  140. // =====================================================================================
  141. #include <gtest/gtest.h>
  142. #include "Arduino.h"
  143. #include "TaskScheduler.h"
  144. // Global test state - used to capture and verify callback executions
  145. std::vector<std::string> test_output;
  146. int callback_counter = 0;
  147. bool onEnable_called = false;
  148. bool onDisable_called = false;
  149. // Global variables for getCurrentTask test
  150. static Task* global_current_task_ptr = nullptr;
  151. static Scheduler* global_scheduler_ptr = nullptr;
  152. // Global variables for yield tests
  153. static Task* global_yield_task = nullptr;
  154. static Task* global_yield_once_task = nullptr;
  155. // Test callback functions
  156. /**
  157. * @brief Callback function for getCurrentTask test
  158. *
  159. * Captures the current task pointer for verification.
  160. */
  161. void current_task_callback() {
  162. global_current_task_ptr = global_scheduler_ptr->getCurrentTask();
  163. test_output.push_back("got_current_task");
  164. }
  165. /**
  166. * @brief Basic callback function for general testing
  167. *
  168. * Simple callback that increments counter and records execution.
  169. * Used for fundamental task execution verification.
  170. */
  171. void basic_callback() {
  172. callback_counter++;
  173. test_output.push_back("basic_callback");
  174. std::cout << "Basic callback executed at " << millis() << "ms" << std::endl;
  175. }
  176. /**
  177. * @brief First test callback for multi-callback scenarios
  178. *
  179. * Used to distinguish between different callbacks in multi-task tests.
  180. */
  181. void callback_1() {
  182. callback_counter++;
  183. test_output.push_back("callback_1");
  184. }
  185. /**
  186. * @brief Second test callback for multi-callback scenarios
  187. *
  188. * Used in coordination tests and callback switching verification.
  189. */
  190. void callback_2() {
  191. callback_counter++;
  192. test_output.push_back("callback_2");
  193. }
  194. /**
  195. * @brief Third test callback for complex scenarios
  196. *
  197. * Additional callback for testing multiple task coordination.
  198. */
  199. void callback_3() {
  200. callback_counter++;
  201. test_output.push_back("callback_3");
  202. }
  203. /**
  204. * @brief Multi-step callback for yield testing - step 1
  205. *
  206. * First phase of multi-step processing for callback switching tests.
  207. */
  208. void multi_step_callback_1() {
  209. test_output.push_back("step_1");
  210. }
  211. /**
  212. * @brief Multi-step callback for yield testing - step 2
  213. *
  214. * Second phase of multi-step processing for callback switching tests.
  215. */
  216. void multi_step_callback_2() {
  217. test_output.push_back("step_2");
  218. }
  219. /**
  220. * @brief Multi-step callback for yield testing - step 3
  221. *
  222. * Third phase of multi-step processing for callback switching tests.
  223. */
  224. void multi_step_callback_3() {
  225. test_output.push_back("step_3");
  226. }
  227. /**
  228. * @brief Callback function for yield test
  229. *
  230. * Yields to multi_step_callback_2 for step-by-step processing.
  231. */
  232. void yield_callback() {
  233. test_output.push_back("step_1");
  234. if (global_yield_task) {
  235. global_yield_task->yield(&multi_step_callback_2);
  236. }
  237. }
  238. /**
  239. * @brief Callback function for yield once test
  240. *
  241. * Yields once to multi_step_callback_2 for single execution.
  242. */
  243. void yield_once_test_callback() {
  244. test_output.push_back("step_1");
  245. if (global_yield_once_task) {
  246. global_yield_once_task->yieldOnce(&multi_step_callback_2);
  247. }
  248. }
  249. /**
  250. * @brief OnEnable lifecycle callback that allows enabling
  251. *
  252. * Returns true to allow task enabling. Used for testing
  253. * normal lifecycle callback behavior.
  254. */
  255. bool test_onEnable() {
  256. onEnable_called = true;
  257. test_output.push_back("onEnable_called");
  258. return true;
  259. }
  260. /**
  261. * @brief OnEnable lifecycle callback that prevents enabling
  262. *
  263. * Returns false to prevent task enabling. Used for testing
  264. * conditional enabling behavior.
  265. */
  266. bool test_onEnable_false() {
  267. onEnable_called = true;
  268. test_output.push_back("onEnable_called_false");
  269. return false;
  270. }
  271. /**
  272. * @brief OnDisable lifecycle callback
  273. *
  274. * Called when task is disabled. Used for testing
  275. * lifecycle callback behavior.
  276. */
  277. void test_onDisable() {
  278. onDisable_called = true;
  279. test_output.push_back("onDisable_called");
  280. }
  281. /**
  282. * @brief Repeating callback for iteration testing
  283. *
  284. * Uses static counter to track execution number, enabling verification
  285. * of proper iteration counting and repeated execution behavior.
  286. */
  287. void repeating_callback() {
  288. static int counter = 0;
  289. counter++;
  290. test_output.push_back("Repeating task #" + std::to_string(counter));
  291. std::cout << "Repeating task #" << counter << " executed at " << millis() << "ms" << std::endl;
  292. }
  293. /**
  294. * @brief Callback for yield switching tests
  295. *
  296. * Records execution and prepares for yield switching behavior.
  297. */
  298. void yield_test_callback() {
  299. test_output.push_back("yield_original");
  300. }
  301. /**
  302. * @brief Callback for yield-once testing
  303. *
  304. * Records execution for yield-once verification.
  305. */
  306. void yield_once_callback() {
  307. test_output.push_back("yield_once_executed");
  308. }
  309. // Test fixture for comprehensive scheduler testing
  310. class SchedulerThoroughTest : public ::testing::Test {
  311. protected:
  312. void SetUp() override {
  313. clearTestOutput();
  314. callback_counter = 0;
  315. onEnable_called = false;
  316. onDisable_called = false;
  317. millis(); // Initialize timing
  318. }
  319. void TearDown() override {
  320. clearTestOutput();
  321. callback_counter = 0;
  322. onEnable_called = false;
  323. onDisable_called = false;
  324. }
  325. bool runSchedulerUntil(Scheduler& ts, std::function<bool()> condition, unsigned long timeout_ms = 1000) {
  326. return waitForCondition([&]() {
  327. ts.execute();
  328. return condition();
  329. }, timeout_ms);
  330. }
  331. };
  332. // ================== TASK CONSTRUCTOR TESTS ==================
  333. /**
  334. * @brief Test Task default constructor behavior
  335. *
  336. * TESTS: Task()
  337. *
  338. * PURPOSE: Verify that a Task created with the default constructor initializes
  339. * all properties to safe default values and is in a predictable state.
  340. *
  341. * EXPECTATIONS:
  342. * - Task should be disabled by default (safety)
  343. * - Interval should be 0 (no automatic execution)
  344. * - Iterations should be 0 (won't execute)
  345. * - RunCounter should be 0 (hasn't executed yet)
  346. *
  347. * IMPORTANCE: Default constructor must create a safe, inert task that won't
  348. * execute unexpectedly. This is critical for safe initialization patterns.
  349. */
  350. TEST_F(SchedulerThoroughTest, TaskDefaultConstructor) {
  351. Task task;
  352. // Verify task is in safe, inert state after default construction
  353. EXPECT_FALSE(task.isEnabled()); // Should not execute without explicit enable
  354. EXPECT_EQ(task.getInterval(), 0); // No automatic timing
  355. EXPECT_EQ(task.getIterations(), 0); // Won't execute without iterations set
  356. EXPECT_EQ(task.getRunCounter(), 0); // No executions yet
  357. }
  358. /**
  359. * @brief Test Task parameterized constructor with all options
  360. *
  361. * TESTS: Task(interval, iterations, callback, scheduler, enable)
  362. *
  363. * PURPOSE: Verify that the full constructor properly sets all task parameters
  364. * and correctly associates the task with a scheduler.
  365. *
  366. * PARAMETERS TESTED:
  367. * - interval: 1000ms (1 second execution interval)
  368. * - iterations: 5 (task will execute 5 times then auto-disable)
  369. * - callback: basic_callback function pointer
  370. * - scheduler: reference to scheduler for automatic addition
  371. * - enable: false (task starts disabled for manual control)
  372. *
  373. * EXPECTATIONS:
  374. * - All parameters should be set exactly as specified
  375. * - Task should be disabled initially (enable=false)
  376. * - RunCounter starts at 0 (no executions yet)
  377. * - Task should be automatically added to scheduler's chain
  378. *
  379. * IMPORTANCE: This tests the most common Task creation pattern and ensures
  380. * all parameters are correctly stored and accessible.
  381. */
  382. TEST_F(SchedulerThoroughTest, TaskParameterizedConstructor) {
  383. Scheduler ts;
  384. Task task(1000, 5, &basic_callback, &ts, false);
  385. // Verify all constructor parameters were set correctly
  386. EXPECT_FALSE(task.isEnabled()); // enable=false parameter
  387. EXPECT_EQ(task.getInterval(), 1000); // 1000ms interval parameter
  388. EXPECT_EQ(task.getIterations(), 5); // 5 iterations parameter
  389. EXPECT_EQ(task.getRunCounter(), 0); // No executions yet
  390. // Note: callback and scheduler assignment tested in execution tests
  391. }
  392. /**
  393. * @brief Test Task constructor with OnEnable/OnDisable callbacks
  394. *
  395. * TESTS: Task(interval, iterations, callback, scheduler, enable, onEnable, onDisable)
  396. *
  397. * PURPOSE: Verify that the full constructor with lifecycle callbacks correctly
  398. * stores callback pointers without invoking them during construction.
  399. *
  400. * LIFECYCLE CALLBACKS:
  401. * - onEnable: Called when task is enabled (should return bool)
  402. * - onDisable: Called when task is disabled
  403. *
  404. * EXPECTATIONS:
  405. * - All basic parameters set correctly
  406. * - Callback pointers stored but not invoked during construction
  407. * - Global callback flags remain false (not called yet)
  408. * - Task ready for enable/disable lifecycle events
  409. *
  410. * IMPORTANCE: Validates that lifecycle callbacks are properly registered
  411. * without premature invocation, ensuring controlled task lifecycle management.
  412. */
  413. TEST_F(SchedulerThoroughTest, TaskConstructorWithOnEnableOnDisable) {
  414. Scheduler ts;
  415. Task task(500, 3, &basic_callback, &ts, false, &test_onEnable, &test_onDisable);
  416. // Verify basic parameters are set correctly
  417. EXPECT_FALSE(task.isEnabled());
  418. EXPECT_EQ(task.getInterval(), 500);
  419. EXPECT_EQ(task.getIterations(), 3);
  420. // Verify lifecycle callbacks are registered but not yet called
  421. EXPECT_FALSE(onEnable_called); // onEnable not called during construction
  422. EXPECT_FALSE(onDisable_called); // onDisable not called during construction
  423. }
  424. /**
  425. * @brief Test Task constructor with automatic enable
  426. *
  427. * TESTS: Task(..., enable=true) + immediate execution behavior
  428. *
  429. * PURPOSE: Verify that tasks created with enable=true are immediately ready
  430. * for execution and will run on the first scheduler pass.
  431. *
  432. * AUTO-ENABLE BEHAVIOR:
  433. * - Task becomes enabled immediately during construction
  434. * - Task is scheduled for immediate execution (no delay)
  435. * - First scheduler pass should execute the task
  436. *
  437. * EXPECTATIONS:
  438. * - Task should be enabled after construction
  439. * - Task should execute successfully on first scheduler run
  440. * - Callback counter should increment correctly
  441. *
  442. * IMPORTANCE: Tests the convenience constructor that creates immediately
  443. * active tasks, commonly used for initialization or startup tasks.
  444. */
  445. TEST_F(SchedulerThoroughTest, TaskConstructorAutoEnabled) {
  446. Scheduler ts;
  447. Task task(100, 1, &basic_callback, &ts, true); // enable=true
  448. // Verify task is enabled immediately after construction
  449. EXPECT_TRUE(task.isEnabled());
  450. // Verify task executes on first scheduler pass
  451. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  452. EXPECT_TRUE(success); // Execution should succeed
  453. EXPECT_EQ(callback_counter, 1); // Should execute exactly once
  454. }
  455. // ================== TASK INFORMATION METHODS TESTS ==================
  456. /**
  457. * @brief Test all Task information getter methods through task lifecycle
  458. *
  459. * TESTS: isEnabled(), getInterval(), getIterations(), getRunCounter()
  460. *
  461. * PURPOSE: Verify that information methods return correct values at different
  462. * stages of task lifecycle and properly track state changes.
  463. *
  464. * METHODS TESTED:
  465. * - isEnabled(): Returns current enabled/disabled state
  466. * - getInterval(): Returns execution interval in milliseconds
  467. * - getIterations(): Returns remaining iterations (decrements after each run)
  468. * - getRunCounter(): Returns total executions since last enable (increments)
  469. *
  470. * LIFECYCLE STAGES TESTED:
  471. * 1. Initial state (after construction)
  472. * 2. After enabling (before execution)
  473. * 3. After first execution (state changes)
  474. *
  475. * EXPECTATIONS:
  476. * - Initial: disabled, interval=2000, iterations=10, runCounter=0
  477. * - After enable: enabled, same interval/iterations, runCounter still 0
  478. * - After execution: enabled, same interval, iterations=9, runCounter=1
  479. *
  480. * IMPORTANCE: These methods are fundamental for task monitoring and debugging.
  481. * Accurate state reporting is critical for application logic and diagnostics.
  482. */
  483. TEST_F(SchedulerThoroughTest, TaskInformationMethods) {
  484. Scheduler ts;
  485. Task task(2000, 10, &basic_callback, &ts, false);
  486. // Test initial state immediately after construction
  487. EXPECT_FALSE(task.isEnabled()); // Should be disabled (enable=false)
  488. EXPECT_EQ(task.getInterval(), 2000); // Should match constructor parameter
  489. EXPECT_EQ(task.getIterations(), 10); // Should match constructor parameter
  490. EXPECT_EQ(task.getRunCounter(), 0); // No executions yet
  491. // Test state after enabling (but before execution)
  492. task.enable();
  493. EXPECT_TRUE(task.isEnabled()); // Should now be enabled
  494. EXPECT_EQ(task.getRunCounter(), 0); // Still 0 before first execution
  495. // Interval and iterations should remain unchanged
  496. // Test state after first execution
  497. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  498. EXPECT_TRUE(success);
  499. EXPECT_EQ(task.getRunCounter(), 1); // Should increment to 1
  500. EXPECT_EQ(task.getIterations(), 9); // Should decrement to 9
  501. // Task should still be enabled and interval unchanged
  502. }
  503. // ================== TASK CONTROL METHODS TESTS ==================
  504. /**
  505. * @brief Test Task enable() and disable() methods with lifecycle callbacks
  506. *
  507. * TESTS: enable(), disable(), isEnabled()
  508. *
  509. * PURPOSE: Verify that enable/disable methods correctly change task state,
  510. * trigger appropriate lifecycle callbacks, and properly control task execution.
  511. *
  512. * CONTROL METHODS TESTED:
  513. * - enable(): Activates task for execution, triggers onEnable callback
  514. * - disable(): Deactivates task, stops execution, triggers onDisable callback
  515. * - isEnabled(): Returns current activation state
  516. *
  517. * LIFECYCLE CALLBACK BEHAVIOR:
  518. * - onEnable(): Called when task transitions from disabled to enabled
  519. * - onDisable(): Called when task transitions from enabled to disabled
  520. * - Callbacks allow custom logic during state transitions
  521. *
  522. * TEST SCENARIOS:
  523. * 1. Enable disabled task (should trigger onEnable)
  524. * 2. Check execution capability (enabled task should execute)
  525. * 3. Disable enabled task (should trigger onDisable, stop execution)
  526. * 4. Verify no execution after disable
  527. *
  528. * EXPECTATIONS:
  529. * - State changes should be immediate and accurate
  530. * - Lifecycle callbacks should be invoked exactly once per transition
  531. * - Execution behavior should match enabled state
  532. *
  533. * IMPORTANCE: Enable/disable is the primary method for dynamic task control.
  534. * This functionality is essential for responsive, event-driven applications.
  535. */
  536. TEST_F(SchedulerThoroughTest, TaskEnableDisable) {
  537. Scheduler ts;
  538. Task task(100, 3, &basic_callback, &ts, false);
  539. // Test enable
  540. EXPECT_FALSE(task.isEnabled());
  541. task.enable();
  542. EXPECT_TRUE(task.isEnabled());
  543. // Test disable
  544. bool prev_state = task.disable();
  545. EXPECT_TRUE(prev_state); // Was enabled
  546. EXPECT_FALSE(task.isEnabled());
  547. // Test disable when already disabled
  548. prev_state = task.disable();
  549. EXPECT_FALSE(prev_state); // Was disabled
  550. EXPECT_FALSE(task.isEnabled());
  551. }
  552. /**
  553. * @brief Test Task enableIfNot() conditional enable method
  554. *
  555. * TESTS: enableIfNot()
  556. *
  557. * PURPOSE: Verify that enableIfNot() provides safe conditional enabling,
  558. * avoiding redundant state changes and providing status feedback.
  559. *
  560. * METHOD BEHAVIOR:
  561. * - enableIfNot(): Enables task only if currently disabled
  562. * - Returns previous enabled state (false = was disabled, true = was enabled)
  563. * - Prevents redundant onEnable callback triggers
  564. * - Idempotent operation (safe to call multiple times)
  565. *
  566. * TEST SCENARIOS:
  567. * 1. Call on disabled task (should enable and return false)
  568. * 2. Call on enabled task (should remain enabled and return true)
  569. *
  570. * EXPECTATIONS:
  571. * - First call: task becomes enabled, returns false (was disabled)
  572. * - Second call: task remains enabled, returns true (was already enabled)
  573. * - No side effects from redundant calls
  574. *
  575. * IMPORTANCE: Conditional enabling prevents unnecessary state transitions
  576. * and provides application feedback about previous state, useful for
  577. * toggle operations and preventing callback storms.
  578. */
  579. TEST_F(SchedulerThoroughTest, TaskEnableIfNot) {
  580. Scheduler ts;
  581. Task task(100, 1, &basic_callback, &ts, false);
  582. // Enable when disabled
  583. bool was_enabled = task.enableIfNot();
  584. EXPECT_FALSE(was_enabled); // Was disabled
  585. EXPECT_TRUE(task.isEnabled());
  586. // Try to enable when already enabled
  587. was_enabled = task.enableIfNot();
  588. EXPECT_TRUE(was_enabled); // Was already enabled
  589. EXPECT_TRUE(task.isEnabled());
  590. }
  591. /**
  592. * @brief Test Task restart() method for resetting task state
  593. *
  594. * TESTS: restart()
  595. *
  596. * PURPOSE: Verify that restart() properly resets task execution state
  597. * while maintaining configuration, allowing tasks to run their full
  598. * iteration cycle again.
  599. *
  600. * METHOD BEHAVIOR:
  601. * - restart(): Resets iteration counter to original value
  602. * - Resets run counter to 0
  603. * - Maintains enabled state
  604. * - Preserves interval and callback configuration
  605. * - Schedules immediate execution (no delay)
  606. *
  607. * TEST SCENARIO:
  608. * 1. Create task with 3 iterations
  609. * 2. Let it execute once (iterations should decrement to 2)
  610. * 3. Call restart() (should reset iterations to 3)
  611. * 4. Verify state is properly reset
  612. *
  613. * EXPECTATIONS:
  614. * - After first execution: getIterations() returns 2
  615. * - After restart(): getIterations() returns original value (3)
  616. * - Task remains enabled throughout
  617. * - Ready for immediate re-execution
  618. *
  619. * IMPORTANCE: Restart functionality enables task recycling and repetitive
  620. * workflows without recreating task objects, essential for state machines
  621. * and cyclic operations.
  622. */
  623. TEST_F(SchedulerThoroughTest, TaskRestart) {
  624. Scheduler ts;
  625. Task task(100, 3, &basic_callback, &ts, true);
  626. // Let it run once
  627. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  628. EXPECT_TRUE(success);
  629. EXPECT_EQ(task.getIterations(), 2); // Should be decremented
  630. // Restart should reset iterations
  631. task.restart();
  632. EXPECT_EQ(task.getIterations(), 3); // Reset to original
  633. EXPECT_TRUE(task.isEnabled());
  634. }
  635. /**
  636. * @brief Test Task restartDelayed() method for delayed task reset
  637. *
  638. * TESTS: restartDelayed(delay)
  639. *
  640. * PURPOSE: Verify that restartDelayed() resets task state like restart()
  641. * but introduces a specified delay before first execution, useful for
  642. * timed restart scenarios and spacing between task cycles.
  643. *
  644. * METHOD BEHAVIOR:
  645. * - restartDelayed(delay): Combines restart() with initial delay
  646. * - Resets iteration and run counters like restart()
  647. * - Schedules first execution after specified delay
  648. * - Subsequent executions follow normal interval timing
  649. * - Task remains enabled but dormant during delay period
  650. *
  651. * TEST SCENARIO:
  652. * 1. Execute task once to modify its state
  653. * 2. Call restartDelayed(200ms)
  654. * 3. Verify no immediate execution (delay period)
  655. * 4. Verify execution occurs after delay expires
  656. *
  657. * EXPECTATIONS:
  658. * - Immediate period: no execution despite scheduler calls
  659. * - After delay: task executes normally with reset state
  660. * - Callback counter increments only after delay period
  661. *
  662. * IMPORTANCE: Delayed restart enables controlled timing gaps between
  663. * task cycles, essential for rate limiting and synchronized multi-task
  664. * restart scenarios.
  665. */
  666. TEST_F(SchedulerThoroughTest, TaskRestartDelayed) {
  667. Scheduler ts;
  668. Task task(50, 2, &basic_callback, &ts, true);
  669. // Let it run once
  670. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  671. EXPECT_TRUE(success);
  672. int count_before_restart = callback_counter;
  673. task.restartDelayed(200); // Restart with 200ms delay
  674. // Should not execute immediately
  675. delay(50);
  676. ts.execute();
  677. EXPECT_EQ(callback_counter, count_before_restart);
  678. // Should execute after delay
  679. delay(200);
  680. success = runSchedulerUntil(ts, [count_before_restart]() {
  681. return callback_counter > count_before_restart;
  682. });
  683. EXPECT_TRUE(success);
  684. }
  685. // ================== TASK TIMING METHODS TESTS ==================
  686. /**
  687. * @brief Test Task delay() method for postponing next execution
  688. *
  689. * TESTS: delay(milliseconds)
  690. *
  691. * PURPOSE: Verify that delay() correctly postpones the next scheduled
  692. * execution by the specified amount, without affecting the task's
  693. * normal interval timing for subsequent executions.
  694. *
  695. * METHOD BEHAVIOR:
  696. * - delay(ms): Postpones next execution by specified milliseconds
  697. * - Affects only the next execution, not the interval permanently
  698. * - Task remains enabled during delay period
  699. * - After delayed execution, normal interval timing resumes
  700. * - Can be called multiple times to accumulate delays
  701. *
  702. * TEST SCENARIO:
  703. * 1. Execute task once to establish baseline
  704. * 2. Call delay(150ms) to postpone next execution
  705. * 3. Verify no execution during delay period (50ms < 150ms)
  706. * 4. Verify execution occurs after delay expires
  707. *
  708. * EXPECTATIONS:
  709. * - During delay: callback counter unchanged despite scheduler calls
  710. * - After delay: task executes and counter increments
  711. * - Subsequent executions follow normal interval
  712. *
  713. * IMPORTANCE: Dynamic delay allows responsive timing adjustments
  714. * based on runtime conditions, essential for adaptive scheduling
  715. * and event-driven timing modifications.
  716. */
  717. TEST_F(SchedulerThoroughTest, TaskDelay) {
  718. Scheduler ts;
  719. Task task(50, 5, &basic_callback, &ts, true);
  720. // Let it run once
  721. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  722. EXPECT_TRUE(success);
  723. int count_before_delay = callback_counter;
  724. task.delay(150); // Delay next execution
  725. // Should not execute immediately
  726. delay(50);
  727. ts.execute();
  728. EXPECT_EQ(callback_counter, count_before_delay);
  729. // Should execute after delay
  730. delay(150);
  731. success = runSchedulerUntil(ts, [count_before_delay]() {
  732. return callback_counter > count_before_delay;
  733. });
  734. EXPECT_TRUE(success);
  735. }
  736. /**
  737. * @brief Test Task forceNextIteration() method for immediate execution
  738. *
  739. * TESTS: forceNextIteration()
  740. *
  741. * PURPOSE: Verify that forceNextIteration() bypasses normal interval timing
  742. * and schedules the task for immediate execution on the next scheduler pass,
  743. * useful for triggering urgent or event-driven task execution.
  744. *
  745. * METHOD BEHAVIOR:
  746. * - forceNextIteration(): Marks task for immediate execution
  747. * - Bypasses remaining interval wait time
  748. * - Does not affect subsequent interval timing
  749. * - Works only if task is enabled
  750. * - Executes on very next scheduler.execute() call
  751. *
  752. * TEST SCENARIO:
  753. * 1. Create task with long interval (1000ms) to prevent natural execution
  754. * 2. Let it execute once (starts long interval timer)
  755. * 3. Call forceNextIteration() during interval wait
  756. * 4. Verify immediate execution on next scheduler pass
  757. *
  758. * EXPECTATIONS:
  759. * - Without force: task would wait full 1000ms interval
  760. * - With force: task executes immediately despite interval
  761. * - Callback counter increments immediately after force
  762. *
  763. * IMPORTANCE: Force execution enables responsive event handling and
  764. * priority task execution, essential for interrupt-driven scenarios
  765. * and urgent task processing.
  766. */
  767. TEST_F(SchedulerThoroughTest, TaskForceNextIteration) {
  768. Scheduler ts;
  769. Task task(1000, 3, &basic_callback, &ts, true); // Long interval
  770. // Execute immediately due to enable
  771. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  772. EXPECT_TRUE(success);
  773. int count_before_force = callback_counter;
  774. task.forceNextIteration();
  775. // Should execute immediately on next scheduler pass
  776. success = runSchedulerUntil(ts, [count_before_force]() {
  777. return callback_counter > count_before_force;
  778. });
  779. EXPECT_TRUE(success);
  780. }
  781. /**
  782. * @brief Test Task enableDelayed() method for delayed activation
  783. *
  784. * TESTS: enableDelayed(delay)
  785. *
  786. * PURPOSE: Verify that enableDelayed() enables a task but delays its
  787. * first execution by the specified amount, useful for coordinated
  788. * task startup and avoiding immediate execution.
  789. *
  790. * METHOD BEHAVIOR:
  791. * - enableDelayed(ms): Enables task but delays first execution
  792. * - Task becomes enabled immediately (isEnabled() returns true)
  793. * - First execution waits for specified delay period
  794. * - After first execution, normal interval timing applies
  795. * - Different from enable() which executes immediately
  796. *
  797. * TEST SCENARIO:
  798. * 1. Create disabled task
  799. * 2. Call enableDelayed(200ms)
  800. * 3. Verify task is enabled but doesn't execute immediately
  801. * 4. Verify execution occurs after delay period
  802. *
  803. * EXPECTATIONS:
  804. * - Task enabled: isEnabled() returns true immediately
  805. * - During delay: no execution despite scheduler calls
  806. * - After delay: task executes normally
  807. *
  808. * IMPORTANCE: Delayed enabling allows coordinated task startup
  809. * sequences and prevents initial execution conflicts, essential
  810. * for synchronized multi-task systems.
  811. */
  812. TEST_F(SchedulerThoroughTest, TaskEnableDelayed) {
  813. Scheduler ts;
  814. Task task(100, 1, &basic_callback, &ts, false);
  815. task.enableDelayed(200);
  816. EXPECT_TRUE(task.isEnabled());
  817. // Should not execute immediately
  818. delay(50);
  819. ts.execute();
  820. EXPECT_EQ(callback_counter, 0);
  821. // Should execute after delay
  822. delay(200);
  823. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  824. EXPECT_TRUE(success);
  825. }
  826. // ================== TASK CONFIGURATION METHODS TESTS ==================
  827. /**
  828. * @brief Test Task set() method for complete task reconfiguration
  829. *
  830. * TESTS: set(interval, iterations, callback, onEnable, onDisable)
  831. *
  832. * PURPOSE: Verify that set() completely reconfigures a task with new
  833. * parameters, allowing task objects to be reused with different
  834. * configurations without recreation.
  835. *
  836. * METHOD BEHAVIOR:
  837. * - set(): Replaces all task configuration parameters
  838. * - Updates interval, iterations, callback, and lifecycle callbacks
  839. * - Does not change enabled state (task remains in current state)
  840. * - Resets internal counters and timing
  841. * - Allows complete task repurposing
  842. *
  843. * TEST SCENARIO:
  844. * 1. Create default task (empty configuration)
  845. * 2. Use set() to configure with specific parameters
  846. * 3. Verify all parameters were set correctly
  847. * 4. Verify task remains disabled (set() doesn't enable)
  848. *
  849. * EXPECTATIONS:
  850. * - All set parameters should be retrievable via getter methods
  851. * - Task should remain disabled (set() doesn't enable)
  852. * - Task ready for enable() to start with new configuration
  853. *
  854. * IMPORTANCE: Set method enables task object reuse and dynamic
  855. * reconfiguration, essential for flexible task management and
  856. * memory-efficient applications.
  857. */
  858. TEST_F(SchedulerThoroughTest, TaskSetMethod) {
  859. Scheduler ts;
  860. Task task;
  861. task.set(300, 7, &basic_callback, &test_onEnable, &test_onDisable);
  862. EXPECT_EQ(task.getInterval(), 300);
  863. EXPECT_EQ(task.getIterations(), 7);
  864. EXPECT_FALSE(task.isEnabled());
  865. }
  866. /**
  867. * @brief Test Task setInterval() method for dynamic timing changes
  868. *
  869. * TESTS: setInterval(newInterval), getInterval()
  870. *
  871. * PURPOSE: Verify that setInterval() dynamically changes task execution
  872. * timing and that changes take effect for subsequent executions,
  873. * enabling adaptive and responsive timing control.
  874. *
  875. * METHOD BEHAVIOR:
  876. * - setInterval(ms): Changes execution interval for future executions
  877. * - Does not affect current timing if task is mid-interval
  878. * - New interval applies to next scheduled execution
  879. * - Can be called multiple times to adjust timing dynamically
  880. * - Allows real-time timing optimization
  881. *
  882. * TEST SCENARIO:
  883. * 1. Create task with initial 100ms interval
  884. * 2. Change interval to 500ms and verify getter
  885. * 3. Let task execute once with new timing
  886. * 4. Change interval to 200ms during execution
  887. * 5. Verify next execution uses new 200ms interval
  888. *
  889. * EXPECTATIONS:
  890. * - getInterval() returns updated value immediately
  891. * - Execution timing reflects new interval settings
  892. * - Dynamic changes affect subsequent executions
  893. *
  894. * IMPORTANCE: Dynamic interval adjustment enables adaptive systems
  895. * that respond to load, priority, or environmental changes in real-time,
  896. * essential for responsive and efficient applications.
  897. */
  898. TEST_F(SchedulerThoroughTest, TaskSetInterval) {
  899. Scheduler ts;
  900. Task task(100, 2, &basic_callback, &ts, true);
  901. task.setInterval(500);
  902. EXPECT_EQ(task.getInterval(), 500);
  903. // Interval change should affect timing
  904. unsigned long start_time = millis();
  905. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  906. EXPECT_TRUE(success);
  907. task.setInterval(200);
  908. int count_before = callback_counter;
  909. success = runSchedulerUntil(ts, [count_before]() {
  910. return callback_counter > count_before;
  911. }, 300);
  912. EXPECT_TRUE(success);
  913. }
  914. /**
  915. * @brief Test Task setIterations() method for dynamic repetition control
  916. *
  917. * TESTS: setIterations(newIterations), getIterations()
  918. *
  919. * PURPOSE: Verify that setIterations() dynamically changes the number of
  920. * remaining executions and that tasks properly auto-disable when reaching
  921. * zero iterations, enabling flexible execution count management.
  922. *
  923. * METHOD BEHAVIOR:
  924. * - setIterations(count): Sets number of remaining executions
  925. * - Count decrements with each execution
  926. * - Task auto-disables when iterations reach zero
  927. * - Can extend or reduce remaining executions dynamically
  928. * - TASK_FOREVER can be set for infinite execution
  929. *
  930. * TEST SCENARIO:
  931. * 1. Create task with initial 2 iterations
  932. * 2. Change to 5 iterations before execution
  933. * 3. Let task run complete cycle (5 executions)
  934. * 4. Verify task auto-disables after completing all iterations
  935. *
  936. * EXPECTATIONS:
  937. * - getIterations() returns updated count
  938. * - Task executes exactly 5 times (new iteration count)
  939. * - Task automatically disables after final iteration + 1
  940. * - Callback counter reaches exactly 5
  941. *
  942. * IMPORTANCE: Dynamic iteration control allows adaptive execution
  943. * cycles based on runtime conditions, essential for conditional
  944. * processing and resource-conscious applications.
  945. * Task is disabled on the next scheduler pass after reaching zero iterations.
  946. */
  947. TEST_F(SchedulerThoroughTest, TaskSetIterations) {
  948. Scheduler ts;
  949. Task task(100, 2, &basic_callback, &ts, true);
  950. task.setIterations(5);
  951. EXPECT_EQ(task.getIterations(), 5);
  952. // Should run 5 times
  953. bool success = runSchedulerUntil(ts, []() { return callback_counter > 5; }, 1000);
  954. // EXPECT_FALSE(success);
  955. EXPECT_EQ(callback_counter, 5);
  956. EXPECT_FALSE(task.isEnabled()); // Should auto-disable after iterations
  957. }
  958. /**
  959. * @brief Test Task callback switching methods for dynamic behavior
  960. *
  961. * TESTS: setCallback(), setOnEnable(), setOnDisable()
  962. *
  963. * PURPOSE: Verify that callback methods can be dynamically changed during
  964. * task lifetime, enabling flexible behavior modification and state-dependent
  965. * functionality without task recreation.
  966. *
  967. * CALLBACK TYPES TESTED:
  968. * - setCallback(): Changes main execution callback function
  969. * - setOnEnable(): Changes lifecycle callback for enable events
  970. * - setOnDisable(): Changes lifecycle callback for disable events
  971. *
  972. * METHOD BEHAVIOR:
  973. * - Callbacks can be changed while task is running
  974. * - New callbacks take effect immediately
  975. * - Previous callbacks are completely replaced
  976. * - Lifecycle callbacks trigger during state transitions
  977. *
  978. * TEST SCENARIO:
  979. * 1. Create task with callback_1 function
  980. * 2. Switch to callback_2 and verify execution uses new callback
  981. * 3. Set lifecycle callbacks and verify they trigger during state changes
  982. *
  983. * EXPECTATIONS:
  984. * - Execution produces output from new callback (callback_2)
  985. * - Enable transition triggers onEnable callback
  986. * - Disable transition triggers onDisable callback
  987. * - All callback switches take effect immediately
  988. *
  989. * IMPORTANCE: Dynamic callback switching enables state machines,
  990. * adaptive behavior, and multi-phase task processing without
  991. * complex conditional logic in callbacks.
  992. */
  993. TEST_F(SchedulerThoroughTest, TaskSetCallbacks) {
  994. Scheduler ts;
  995. Task task(100, 2, &callback_1, &ts, false);
  996. // Test setCallback
  997. task.setCallback(&callback_2);
  998. task.enable();
  999. bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
  1000. EXPECT_TRUE(success);
  1001. EXPECT_EQ(getTestOutput(0), "callback_2");
  1002. // Test setOnEnable and setOnDisable
  1003. task.setOnEnable(&test_onEnable);
  1004. task.setOnDisable(&test_onDisable);
  1005. task.disable();
  1006. task.enable();
  1007. EXPECT_TRUE(onEnable_called);
  1008. task.disable();
  1009. EXPECT_TRUE(onDisable_called);
  1010. }
  1011. // ================== TASK ITERATION STATE TESTS ==================
  1012. /**
  1013. * @brief Test Task iteration state query methods for execution context
  1014. *
  1015. * TESTS: isFirstIteration(), isLastIteration()
  1016. *
  1017. * PURPOSE: Verify that iteration state query methods correctly identify
  1018. * the execution context within a task's iteration cycle, enabling
  1019. * conditional logic based on iteration position.
  1020. *
  1021. * STATE METHODS TESTED:
  1022. * - isFirstIteration(): Returns true only during the first execution
  1023. * - isLastIteration(): Returns true only during the final execution
  1024. * - Both provide context for conditional callback behavior
  1025. *
  1026. * TEST SCENARIO:
  1027. * 1. Create task with 3 iterations
  1028. * 2. Execute first iteration (implicit due to enable=true)
  1029. * 3. Use modified callback to check states during iterations 2 and 3
  1030. * 4. Verify correct state reporting for middle and final iterations
  1031. *
  1032. * EXPECTATIONS:
  1033. * - First iteration: isFirstIteration()=true, isLastIteration()=false
  1034. * - Middle iteration: isFirstIteration()=false, isLastIteration()=false
  1035. * - Final iteration: isFirstIteration()=false, isLastIteration()=true
  1036. *
  1037. * IMPORTANCE: Iteration state queries enable initialization and cleanup
  1038. * logic within callbacks, essential for resource management and
  1039. * conditional processing based on execution phase.
  1040. * NOTE: Scheduler disables the task on the next scheduler pass after the final iteration.
  1041. */
  1042. TEST_F(SchedulerThoroughTest, TaskIterationState) {
  1043. Scheduler ts;
  1044. Task task(100, 3, &basic_callback, &ts, true);
  1045. // First iteration
  1046. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  1047. EXPECT_TRUE(success);
  1048. // Note: Testing iteration state requires access during callback execution
  1049. // This test verifies the task completes its iterations properly
  1050. success = runSchedulerUntil(ts, []() { return false; }, 1000);
  1051. // EXPECT_TRUE(success);
  1052. EXPECT_EQ(callback_counter, 3);
  1053. EXPECT_FALSE(task.isEnabled()); // Should auto-disable after 3 iterations
  1054. }
  1055. // ================== TASK CALLBACK SWITCHING TESTS ==================
  1056. /**
  1057. * @brief Test Task yield() method for dynamic callback switching
  1058. *
  1059. * TESTS: yield(newCallback)
  1060. *
  1061. * PURPOSE: Verify that yield() permanently switches the task's callback
  1062. * function to a new function, enabling state machine behavior and
  1063. * multi-phase task processing within a single task object.
  1064. *
  1065. * METHOD BEHAVIOR:
  1066. * - yield(callback): Permanently changes task's callback function
  1067. * - Switch takes effect immediately
  1068. * - All subsequent executions use the new callback
  1069. * - Original callback is completely replaced
  1070. * - Enables state machine and multi-step processing patterns
  1071. *
  1072. * TEST SCENARIO:
  1073. * 1. Create task with multi_step_callback_1
  1074. * 2. In callback, switch to multi_step_callback_2 via yield
  1075. * 3. Verify first execution produces "step_1" output
  1076. * 4. Verify second execution produces "step_2" output (new callback)
  1077. *
  1078. * EXPECTATIONS:
  1079. * - First execution: "step_1" output from original callback
  1080. * - Yield call: switches to multi_step_callback_2
  1081. * - Second execution: "step_2" output from new callback
  1082. * - All future executions use new callback
  1083. *
  1084. * IMPORTANCE: Yield enables complex state machines and multi-phase
  1085. * processing within single tasks, essential for sequential operations
  1086. * and adaptive task behavior.
  1087. */
  1088. TEST_F(SchedulerThoroughTest, TaskYield) {
  1089. Scheduler ts;
  1090. Task task(200, 3, yield_callback, &ts, true);
  1091. global_yield_task = &task;
  1092. // First execution should produce step_1 and yield
  1093. bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
  1094. EXPECT_TRUE(success);
  1095. EXPECT_EQ(getTestOutput(0), "step_1");
  1096. // Second execution should produce step_2 from yielded callback
  1097. success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
  1098. EXPECT_TRUE(success);
  1099. EXPECT_EQ(getTestOutput(1), "step_2");
  1100. global_yield_task = nullptr; // Clean up
  1101. }
  1102. /**
  1103. * @brief Test Task yieldOnce() method for single callback switch
  1104. *
  1105. * TESTS: yieldOnce(newCallback)
  1106. *
  1107. * PURPOSE: Verify that yieldOnce() switches to a new callback for exactly
  1108. * one execution, then automatically disables the task, enabling one-time
  1109. * completion or finalization logic.
  1110. *
  1111. * METHOD BEHAVIOR:
  1112. * - yieldOnce(callback): Switches to new callback for one execution only
  1113. * - New callback executes exactly once on next scheduler pass
  1114. * - Task automatically disables after the single execution
  1115. * - Original callback is not restored (task becomes inactive)
  1116. * - Useful for completion/cleanup logic and one-shot operations
  1117. *
  1118. * TEST SCENARIO:
  1119. * 1. Create task with 5 iterations and initial callback
  1120. * 2. In first execution, call yieldOnce() to switch to completion callback
  1121. * 3. Verify first execution produces "step_1" output
  1122. * 4. Verify second execution produces "step_2" output
  1123. * 5. Verify task automatically disables after yieldOnce execution
  1124. *
  1125. * EXPECTATIONS:
  1126. * - First execution: "step_1" from original callback with yieldOnce call
  1127. * - Second execution: "step_2" from yielded callback
  1128. * - After second execution: task becomes disabled automatically
  1129. * - No further executions occur
  1130. *
  1131. * IMPORTANCE: YieldOnce enables one-time finalization, cleanup, and
  1132. * completion logic, essential for graceful task termination and
  1133. * resource cleanup patterns.
  1134. */
  1135. TEST_F(SchedulerThoroughTest, TaskYieldOnce) {
  1136. Scheduler ts;
  1137. Task task(100, 5, yield_once_test_callback, &ts, true);
  1138. global_yield_once_task = &task;
  1139. bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
  1140. EXPECT_TRUE(success);
  1141. // Should execute step 2 once then disable
  1142. success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
  1143. EXPECT_TRUE(success);
  1144. EXPECT_EQ(getTestOutput(1), "step_2");
  1145. EXPECT_TRUE(task.isEnabled()); // Should be still enabled after yieldOnce
  1146. global_yield_once_task = nullptr; // Clean up
  1147. }
  1148. // ================== TASK ONENABLE/ONDISABLE TESTS ==================
  1149. /**
  1150. * @brief Test Task OnEnable/OnDisable lifecycle callback behavior
  1151. *
  1152. * TESTS: onEnable callback, onDisable callback, lifecycle triggers
  1153. *
  1154. * PURPOSE: Verify that lifecycle callbacks are properly invoked during
  1155. * task state transitions, enabling initialization and cleanup logic
  1156. * to execute at appropriate times.
  1157. *
  1158. * LIFECYCLE CALLBACK BEHAVIOR:
  1159. * - onEnable(): Called when task transitions from disabled to enabled
  1160. * - onDisable(): Called when task transitions from enabled to disabled
  1161. * - Callbacks execute synchronously during state change
  1162. * - Enable callback can prevent enabling by returning false
  1163. * - Disable callback is informational (no return value)
  1164. *
  1165. * TEST SCENARIO:
  1166. * 1. Create task with lifecycle callbacks but start disabled
  1167. * 2. Enable task and verify onEnable callback is invoked
  1168. * 3. Disable task and verify onDisable callback is invoked
  1169. * 4. Verify callbacks are triggered exactly once per transition
  1170. *
  1171. * EXPECTATIONS:
  1172. * - OnEnable called during enable() transition
  1173. * - OnDisable called during disable() transition
  1174. * - Global callback flags properly set
  1175. * - Task state changes successful
  1176. *
  1177. * IMPORTANCE: Lifecycle callbacks enable proper resource management,
  1178. * initialization, and cleanup, essential for robust task lifecycle
  1179. * management and preventing resource leaks.
  1180. */
  1181. TEST_F(SchedulerThoroughTest, TaskOnEnableOnDisable) {
  1182. Scheduler ts;
  1183. Task task(100, 2, &basic_callback, &ts, false, &test_onEnable, &test_onDisable);
  1184. // Test OnEnable
  1185. task.enable();
  1186. EXPECT_TRUE(onEnable_called);
  1187. EXPECT_TRUE(task.isEnabled());
  1188. // Reset and test OnDisable
  1189. onEnable_called = false;
  1190. task.disable();
  1191. EXPECT_TRUE(onDisable_called);
  1192. EXPECT_FALSE(task.isEnabled());
  1193. }
  1194. /**
  1195. * @brief Test Task OnEnable callback returning false to prevent enabling
  1196. *
  1197. * TESTS: onEnable callback return value, conditional enabling
  1198. *
  1199. * PURPOSE: Verify that when an onEnable callback returns false, the task
  1200. * remains disabled, providing a mechanism for conditional enabling based
  1201. * on runtime conditions or resource availability.
  1202. *
  1203. * CONDITIONAL ENABLING BEHAVIOR:
  1204. * - onEnable(): Must return boolean (true = allow enable, false = prevent)
  1205. * - When returns false: task remains disabled despite enable() call
  1206. * - When returns true: task becomes enabled normally
  1207. * - Callback is always invoked (for logging/monitoring purposes)
  1208. * - Enables conditional logic for resource-dependent tasks
  1209. *
  1210. * TEST SCENARIO:
  1211. * 1. Create task with onEnable callback that returns false
  1212. * 2. Call enable() on the task
  1213. * 3. Verify onEnable callback was invoked
  1214. * 4. Verify task remains disabled (enable() call rejected)
  1215. *
  1216. * EXPECTATIONS:
  1217. * - OnEnable callback is called (onEnable_called flag set)
  1218. * - Task remains disabled (isEnabled() returns false)
  1219. * - Enable attempt is gracefully rejected
  1220. *
  1221. * IMPORTANCE: Conditional enabling prevents tasks from starting when
  1222. * prerequisites aren't met, essential for resource-dependent operations
  1223. * and safety-critical system states.
  1224. */
  1225. TEST_F(SchedulerThoroughTest, TaskOnEnableReturnsFalse) {
  1226. Scheduler ts;
  1227. Task task(100, 1, &basic_callback, &ts, false, &test_onEnable_false, &test_onDisable);
  1228. // OnEnable returns false, task should remain disabled
  1229. task.enable();
  1230. EXPECT_TRUE(onEnable_called);
  1231. EXPECT_FALSE(task.isEnabled()); // Should remain disabled
  1232. }
  1233. // ================== SCHEDULER CONSTRUCTOR AND INIT TESTS ==================
  1234. /**
  1235. * @brief Test Scheduler constructor and basic operation
  1236. *
  1237. * TESTS: Scheduler(), execute() with no tasks
  1238. *
  1239. * PURPOSE: Verify that a Scheduler object can be constructed successfully
  1240. * and can safely execute even when no tasks are registered, demonstrating
  1241. * robustness in edge cases.
  1242. *
  1243. * CONSTRUCTOR BEHAVIOR:
  1244. * - Scheduler(): Creates empty scheduler with no tasks
  1245. * - execute(): Safely handles empty task list
  1246. * - No exceptions or crashes in degenerate cases
  1247. * - Ready to accept tasks via Task constructor registration
  1248. *
  1249. * TEST SCENARIO:
  1250. * 1. Create empty scheduler
  1251. * 2. Call execute() on empty scheduler
  1252. * 3. Verify no output generated (no tasks to execute)
  1253. * 4. Verify no crashes or exceptions
  1254. *
  1255. * EXPECTATIONS:
  1256. * - Constructor succeeds without exceptions
  1257. * - Execute runs safely with no tasks
  1258. * - No test output produced (counter remains 0)
  1259. * - Scheduler ready for task registration
  1260. *
  1261. * IMPORTANCE: Empty scheduler robustness ensures safe operation during
  1262. * initialization phases and prevents crashes in edge cases, essential
  1263. * for reliable system startup sequences.
  1264. */
  1265. TEST_F(SchedulerThoroughTest, SchedulerConstructor) {
  1266. Scheduler ts;
  1267. // Should be able to execute without tasks
  1268. ts.execute();
  1269. EXPECT_EQ(getTestOutputCount(), 0);
  1270. }
  1271. /**
  1272. * @brief Test Scheduler init() method for reinitialization
  1273. *
  1274. * TESTS: init()
  1275. *
  1276. * PURPOSE: Verify that init() properly reinitializes the scheduler
  1277. * state while preserving task registrations, enabling scheduler
  1278. * reset without losing configured tasks.
  1279. *
  1280. * INIT METHOD BEHAVIOR:
  1281. * - init(): Reinitializes internal scheduler state
  1282. * - Preserves registered tasks and their configurations
  1283. * - Resets timing and execution state
  1284. * - Tasks remain functional after reinitialization
  1285. * - Useful for system restart scenarios
  1286. *
  1287. * TEST SCENARIO:
  1288. * 1. Create scheduler with one registered task
  1289. * 2. Call init() to reinitialize scheduler
  1290. * 3. Verify task still executes properly after init
  1291. * 4. Confirm scheduler functionality is preserved
  1292. *
  1293. * EXPECTATIONS:
  1294. * - Init() completes without errors
  1295. * - Task remains registered and functional
  1296. * - Task executes successfully after reinitialization
  1297. * - Callback counter increments as expected
  1298. *
  1299. * IMPORTANCE: Scheduler reinitialization enables system restart
  1300. * scenarios and state recovery while preserving task configurations,
  1301. * essential for robust and recoverable applications.
  1302. */
  1303. TEST_F(SchedulerThoroughTest, SchedulerInit) {
  1304. Scheduler ts;
  1305. Task task(100, 1, &basic_callback, &ts, true);
  1306. ts.init(); // Should reinitialize
  1307. // Task should not work after init - the task chain is empty
  1308. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
  1309. EXPECT_FALSE(success);
  1310. }
  1311. // ================== SCHEDULER TASK MANAGEMENT TESTS ==================
  1312. /**
  1313. * @brief Test Scheduler addTask() and deleteTask() methods for manual task management
  1314. *
  1315. * TESTS: addTask(task), deleteTask(task)
  1316. *
  1317. * PURPOSE: Verify that tasks can be manually added to and removed from
  1318. * schedulers, enabling dynamic task management and scheduler composition
  1319. * patterns beyond automatic constructor registration.
  1320. *
  1321. * TASK MANAGEMENT BEHAVIOR:
  1322. * - addTask(): Manually registers task with scheduler
  1323. * - deleteTask(): Removes task from scheduler's management
  1324. * - Tasks can be added without using scheduler parameter in constructor
  1325. * - Deleted tasks are no longer executed by scheduler
  1326. * - Remaining tasks continue normal operation
  1327. *
  1328. * TEST SCENARIO:
  1329. * 1. Create tasks without scheduler assignment (nullptr)
  1330. * 2. Manually add both tasks to scheduler
  1331. * 3. Enable and execute both tasks
  1332. * 4. Delete one task from scheduler
  1333. * 5. Verify only remaining task executes
  1334. *
  1335. * EXPECTATIONS:
  1336. * - Both tasks execute when added to scheduler
  1337. * - After deletion, only non-deleted task executes
  1338. * - Deleted task becomes unmanaged (won't execute)
  1339. * - Scheduler continues operating with remaining tasks
  1340. *
  1341. * IMPORTANCE: Manual task management enables dynamic scheduler composition,
  1342. * conditional task registration, and runtime task lifecycle management,
  1343. * essential for flexible and adaptive applications.
  1344. */
  1345. TEST_F(SchedulerThoroughTest, SchedulerAddDeleteTask) {
  1346. Scheduler ts;
  1347. Task task1(100, 1, &callback_1, nullptr, false);
  1348. Task task2(150, 1, &callback_2, nullptr, false);
  1349. // Add tasks manually
  1350. ts.addTask(task1);
  1351. ts.addTask(task2);
  1352. task1.enable();
  1353. task2.enable();
  1354. bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
  1355. EXPECT_TRUE(success);
  1356. // Delete task1
  1357. ts.deleteTask(task1);
  1358. // Only task2 should be manageable now
  1359. clearTestOutput();
  1360. task2.restart();
  1361. success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
  1362. EXPECT_TRUE(success);
  1363. EXPECT_EQ(getTestOutput(0), "callback_2");
  1364. }
  1365. // ================== SCHEDULER EXECUTION CONTROL TESTS ==================
  1366. /**
  1367. * @brief Test Scheduler execute() method return value for idle detection
  1368. *
  1369. * TESTS: execute() return value, idle state detection
  1370. *
  1371. * PURPOSE: Verify that execute() correctly returns idle status, enabling
  1372. * applications to detect when no tasks are ready for execution and
  1373. * implement power management or alternative processing.
  1374. *
  1375. * EXECUTE RETURN VALUE BEHAVIOR:
  1376. * - execute(): Returns false when tasks executed, true when idle
  1377. * - Idle = no tasks ready for execution at current time
  1378. * - Non-idle = one or more tasks executed during call
  1379. * - Enables power management and conditional processing
  1380. * - Critical for battery-powered and real-time applications
  1381. *
  1382. * TEST SCENARIO:
  1383. * 1. Create two tasks with different intervals (100ms, 150ms)
  1384. * 2. First execute() should be non-idle (immediate execution)
  1385. * 3. Run until both tasks complete
  1386. * 4. Later execute() should be idle (no tasks ready)
  1387. *
  1388. * EXPECTATIONS:
  1389. * - First execute(): returns false (tasks executed)
  1390. * - Both tasks produce expected output
  1391. * - After completion: execute() returns true (idle)
  1392. *
  1393. * IMPORTANCE: Idle detection enables efficient resource utilization,
  1394. * power management, and responsive application behavior, essential
  1395. * for embedded and battery-powered systems.
  1396. */
  1397. TEST_F(SchedulerThoroughTest, SchedulerExecute) {
  1398. Scheduler ts;
  1399. Task task1(100, 1, &callback_1, &ts, true);
  1400. Task task2(150, 1, &callback_2, &ts, true);
  1401. bool idle = ts.execute();
  1402. // First execute should run tasks immediately, so not idle
  1403. EXPECT_FALSE(idle);
  1404. bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
  1405. EXPECT_TRUE(success);
  1406. // After tasks complete, should be idle
  1407. delay(200);
  1408. idle = ts.execute();
  1409. EXPECT_TRUE(idle);
  1410. }
  1411. /**
  1412. * @brief Test Scheduler enableAll() and disableAll() methods for bulk control
  1413. *
  1414. * TESTS: enableAll(), disableAll()
  1415. *
  1416. * PURPOSE: Verify that enableAll() and disableAll() methods correctly
  1417. * change the state of all tasks managed by the scheduler, enabling
  1418. * coordinated system-wide task control.
  1419. *
  1420. * BULK CONTROL BEHAVIOR:
  1421. * - enableAll(): Enables every task registered with scheduler
  1422. * - disableAll(): Disables every task registered with scheduler
  1423. * - Affects all tasks regardless of current state
  1424. * - Triggers onEnable/onDisable callbacks for each task
  1425. * - Enables system-wide start/stop functionality
  1426. *
  1427. * TEST SCENARIO:
  1428. * 1. Create three tasks in disabled state
  1429. * 2. Call enableAll() and verify all tasks become enabled
  1430. * 3. Call disableAll() and verify all tasks become disabled
  1431. * 4. Confirm state changes affect all registered tasks
  1432. *
  1433. * EXPECTATIONS:
  1434. * - EnableAll(): all tasks report isEnabled() = true
  1435. * - DisableAll(): all tasks report isEnabled() = false
  1436. * - State changes are consistent across all tasks
  1437. * - Bulk operations affect entire task set
  1438. *
  1439. * IMPORTANCE: Bulk enable/disable enables system-wide control patterns,
  1440. * emergency stops, coordinated startup/shutdown, and mode switching,
  1441. * essential for complex multi-task systems.
  1442. */
  1443. TEST_F(SchedulerThoroughTest, SchedulerEnableDisableAll) {
  1444. Scheduler ts;
  1445. Task task1(100, 1, &callback_1, &ts, false);
  1446. Task task2(150, 1, &callback_2, &ts, false);
  1447. Task task3(200, 1, &callback_3, &ts, false);
  1448. // Enable all
  1449. ts.enableAll();
  1450. EXPECT_TRUE(task1.isEnabled());
  1451. EXPECT_TRUE(task2.isEnabled());
  1452. EXPECT_TRUE(task3.isEnabled());
  1453. // Disable all
  1454. ts.disableAll();
  1455. EXPECT_FALSE(task1.isEnabled());
  1456. EXPECT_FALSE(task2.isEnabled());
  1457. EXPECT_FALSE(task3.isEnabled());
  1458. }
  1459. // ================== SCHEDULER TIME QUERY TESTS ==================
  1460. /**
  1461. * @brief Test Scheduler timeUntilNextIteration() method for timing queries
  1462. *
  1463. * TESTS: timeUntilNextIteration(task)
  1464. *
  1465. * PURPOSE: Verify that timeUntilNextIteration() accurately reports the
  1466. * remaining time before a task's next scheduled execution, enabling
  1467. * predictive scheduling and timing-based application logic.
  1468. *
  1469. * TIME QUERY BEHAVIOR:
  1470. * - timeUntilNextIteration(): Returns milliseconds until next execution
  1471. * - Returns -1 for disabled tasks (not scheduled)
  1472. * - Returns positive value for enabled tasks with pending execution
  1473. * - Value decreases as time passes toward execution
  1474. * - Enables predictive timing and coordination
  1475. *
  1476. * TEST SCENARIO:
  1477. * 1. Check disabled task (should return -1)
  1478. * 2. Enable task with 500ms delay
  1479. * 3. Verify time remaining is close to 500ms
  1480. * 4. Wait 200ms and verify time decreased appropriately
  1481. *
  1482. * EXPECTATIONS:
  1483. * - Disabled task: returns -1
  1484. * - Initially delayed task: returns ~500ms
  1485. * - After 200ms delay: returns ~300ms
  1486. * - Timing accuracy within reasonable bounds
  1487. *
  1488. * IMPORTANCE: Time queries enable predictive scheduling, coordination
  1489. * between tasks, and timing-based application logic, essential for
  1490. * real-time and synchronized operations.
  1491. */
  1492. TEST_F(SchedulerThoroughTest, SchedulerTimeUntilNextIteration) {
  1493. Scheduler ts;
  1494. Task task(1000, 1, &basic_callback, &ts, false);
  1495. // Disabled task should return -1
  1496. long time_until = ts.timeUntilNextIteration(task);
  1497. EXPECT_EQ(time_until, -1);
  1498. // Enabled task with delay
  1499. task.enableDelayed(500);
  1500. time_until = ts.timeUntilNextIteration(task);
  1501. EXPECT_GT(time_until, 400); // Should be close to 500ms
  1502. EXPECT_LE(time_until, 500);
  1503. // After some time passes
  1504. delay(200);
  1505. time_until = ts.timeUntilNextIteration(task);
  1506. EXPECT_GT(time_until, 200); // Should be around 300ms
  1507. EXPECT_LE(time_until, 300);
  1508. }
  1509. // ================== SCHEDULER TASK ACCESS TESTS ==================
  1510. /**
  1511. * @brief Test Scheduler getCurrentTask() method for callback context identification
  1512. *
  1513. * TESTS: getCurrentTask()
  1514. *
  1515. * PURPOSE: Verify that getCurrentTask() correctly returns a pointer to the
  1516. * currently executing task when called from within a task callback,
  1517. * enabling self-referential task operations and context awareness.
  1518. *
  1519. * CONTEXT ACCESS BEHAVIOR:
  1520. * - getCurrentTask(): Returns pointer to currently executing task
  1521. * - Only valid when called from within task callback
  1522. * - Returns nullptr when called outside task execution context
  1523. * - Enables task self-modification and context-aware operations
  1524. * - Critical for advanced task patterns and introspection
  1525. *
  1526. * TEST SCENARIO:
  1527. * 1. Create task with callback that calls getCurrentTask()
  1528. * 2. Execute task and capture the returned task pointer
  1529. * 3. Verify returned pointer matches the actual task object
  1530. * 4. Confirm context identification works correctly
  1531. *
  1532. * EXPECTATIONS:
  1533. * - getCurrentTask() returns valid pointer during execution
  1534. * - Returned pointer equals address of actual task object
  1535. * - Context identification is accurate and reliable
  1536. *
  1537. * IMPORTANCE: Current task access enables advanced patterns like
  1538. * task self-modification, recursive operations, and context-aware
  1539. * callback behavior, essential for sophisticated task orchestration.
  1540. */
  1541. TEST_F(SchedulerThoroughTest, SchedulerCurrentTask) {
  1542. Scheduler ts;
  1543. Task* task_address = nullptr;
  1544. // Set up global pointers for the callback
  1545. global_current_task_ptr = nullptr;
  1546. global_scheduler_ptr = &ts;
  1547. Task task(100, 1, current_task_callback, &ts, true);
  1548. task_address = &task;
  1549. bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
  1550. EXPECT_TRUE(success);
  1551. EXPECT_EQ(global_current_task_ptr, task_address);
  1552. // Clean up global pointers
  1553. global_current_task_ptr = nullptr;
  1554. global_scheduler_ptr = nullptr;
  1555. }
  1556. // ================== SCHEDULER TIMING CONTROL TESTS ==================
  1557. /**
  1558. * @brief Test Scheduler startNow() method for immediate execution trigger
  1559. *
  1560. * TESTS: startNow()
  1561. *
  1562. * PURPOSE: Verify that startNow() forces all enabled tasks to execute
  1563. * immediately on the next scheduler pass, regardless of their current
  1564. * timing state, enabling synchronized restart and emergency execution.
  1565. *
  1566. * START NOW BEHAVIOR:
  1567. * - startNow(): Forces immediate execution of all enabled tasks
  1568. * - Bypasses all interval and delay timing
  1569. * - Affects all tasks managed by scheduler
  1570. * - Useful for synchronized restart and emergency execution
  1571. * - Tasks resume normal timing after startNow execution
  1572. *
  1573. * TEST SCENARIO:
  1574. * 1. Create tasks with long intervals that would normally delay execution
  1575. * 2. Let them execute once, then restart with delays
  1576. * 3. Call startNow() to bypass delays
  1577. * 4. Verify all tasks execute immediately despite delays
  1578. *
  1579. * EXPECTATIONS:
  1580. * - Initial execution: tasks run due to enable()
  1581. * - After restart+delay: tasks would normally wait
  1582. * - After startNow(): all tasks execute immediately
  1583. * - All expected outputs are produced
  1584. *
  1585. * IMPORTANCE: StartNow enables synchronized system restart, emergency
  1586. * execution, and coordinated task triggering, essential for real-time
  1587. * systems and emergency response scenarios.
  1588. */
  1589. TEST_F(SchedulerThoroughTest, SchedulerStartNow) {
  1590. Scheduler ts;
  1591. Task task1(1000, 1, &callback_1, &ts, true); // Long interval
  1592. Task task2(2000, 1, &callback_2, &ts, true); // Even longer interval
  1593. // Tasks should execute immediately due to enable(), let them
  1594. bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
  1595. EXPECT_TRUE(success);
  1596. clearTestOutput();
  1597. // Restart tasks with long intervals
  1598. task1.restart();
  1599. task2.restart();
  1600. task1.delay(1000); // Delay them
  1601. task2.delay(2000);
  1602. // startNow should make them execute immediately
  1603. ts.startNow();
  1604. success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
  1605. EXPECT_TRUE(success);
  1606. }
  1607. // ================== INTEGRATION TESTS ==================
  1608. /**
  1609. * @brief Integration test for complete task lifecycle with all features
  1610. *
  1611. * TESTS: Full task lifecycle including enable, execute, auto-disable, restart
  1612. *
  1613. * PURPOSE: Verify that all task features work together correctly in a
  1614. * realistic usage scenario, testing the integration of multiple methods
  1615. * and lifecycle events in sequence.
  1616. *
  1617. * LIFECYCLE INTEGRATION TESTED:
  1618. * - Initial state verification (disabled, zero counters)
  1619. * - Enable with onEnable callback triggering
  1620. * - Multiple executions with proper counter tracking
  1621. * - Auto-disable after completing all iterations
  1622. * - OnDisable callback triggering after completion
  1623. * - Restart functionality with state reset
  1624. *
  1625. * TEST SCENARIO:
  1626. * 1. Create task with 3 iterations and lifecycle callbacks
  1627. * 2. Verify initial disabled state
  1628. * 3. Enable and verify onEnable callback
  1629. * 4. Execute all 3 iterations and verify counters
  1630. * 5. Verify auto-disable and onDisable callback
  1631. * 6. Restart and verify reset state and re-enable
  1632. *
  1633. * EXPECTATIONS:
  1634. * - All lifecycle callbacks triggered at correct times
  1635. * - Execution and iteration counters track correctly
  1636. * - Auto-disable occurs after completing iterations
  1637. * - Restart properly resets state and re-enables
  1638. *
  1639. * IMPORTANCE: Integration testing validates that all features work
  1640. * together as designed, ensuring reliable operation in real applications
  1641. * with complex task lifecycle requirements.
  1642. */
  1643. TEST_F(SchedulerThoroughTest, ComplexTaskLifecycle) {
  1644. Scheduler ts;
  1645. Task task(200, 3, &basic_callback, &ts, false, &test_onEnable, &test_onDisable);
  1646. // Full lifecycle test
  1647. EXPECT_FALSE(task.isEnabled());
  1648. EXPECT_EQ(task.getRunCounter(), 0);
  1649. // Enable and run
  1650. task.enable();
  1651. EXPECT_TRUE(onEnable_called);
  1652. EXPECT_TRUE(task.isEnabled());
  1653. // Run all iterations
  1654. bool success = runSchedulerUntil(ts, []() { return false; }, 1000);
  1655. // EXPECT_TRUE(success);
  1656. EXPECT_EQ(callback_counter, 3);
  1657. EXPECT_EQ(task.getRunCounter(), 3);
  1658. EXPECT_FALSE(task.isEnabled()); // Auto-disabled after iterations
  1659. EXPECT_TRUE(onDisable_called);
  1660. // Restart and verify
  1661. onEnable_called = false;
  1662. onDisable_called = false;
  1663. task.restart();
  1664. EXPECT_TRUE(onEnable_called);
  1665. EXPECT_TRUE(task.isEnabled());
  1666. EXPECT_EQ(task.getIterations(), 3); // Reset
  1667. }
  1668. /**
  1669. * @brief Integration test for multiple concurrent tasks with different timing
  1670. *
  1671. * TESTS: Multiple tasks with different enable timing and execution patterns
  1672. *
  1673. * PURPOSE: Verify that the scheduler correctly manages multiple concurrent
  1674. * tasks with different timing characteristics, ensuring proper execution
  1675. * order and no interference between tasks.
  1676. *
  1677. * MULTI-TASK INTEGRATION TESTED:
  1678. * - Three tasks with different intervals (100ms, 150ms, 200ms)
  1679. * - Different enable timing: immediate, 50ms delay, 100ms delay
  1680. * - Each task executes 2 iterations (6 total executions)
  1681. * - Proper execution ordering based on timing
  1682. * - No task interference or missed executions
  1683. *
  1684. * TEST SCENARIO:
  1685. * 1. Create three tasks with different intervals and 2 iterations each
  1686. * 2. Enable task1 immediately, task2 after 50ms, task3 after 100ms
  1687. * 3. Run until all 6 executions complete (2 per task)
  1688. * 4. Verify first execution follows enable timing order
  1689. * 5. Confirm all tasks complete their iterations
  1690. *
  1691. * EXPECTATIONS:
  1692. * - All 6 executions complete successfully
  1693. * - First execution is from task1 (immediate enable)
  1694. * - Total execution count reaches 6
  1695. * - No tasks interfere with each other
  1696. *
  1697. * IMPORTANCE: Multi-task integration validates scheduler's core
  1698. * functionality under realistic concurrent workloads, ensuring
  1699. * reliable operation in complex applications.
  1700. */
  1701. TEST_F(SchedulerThoroughTest, MultipleTasksInteraction) {
  1702. Scheduler ts;
  1703. Task task1(100, 2, &callback_1, &ts, false);
  1704. Task task2(150, 2, &callback_2, &ts, false);
  1705. Task task3(200, 2, &callback_3, &ts, false);
  1706. // Enable with different timings
  1707. task1.enable();
  1708. task2.enableDelayed(50);
  1709. task3.enableDelayed(100);
  1710. // All should execute their iterations
  1711. bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 6; });
  1712. EXPECT_TRUE(success);
  1713. // Verify execution order (first executions should follow timing)
  1714. EXPECT_EQ(getTestOutput(0), "callback_1"); // Immediate
  1715. // Subsequent order may vary due to intervals
  1716. }
  1717. // ================== EDGE CASES AND ERROR HANDLING ==================
  1718. /**
  1719. * @brief Edge case test for task with zero iterations
  1720. *
  1721. * TESTS: Task behavior with 0 iterations
  1722. *
  1723. * PURPOSE: Verify that tasks created with zero iterations behave correctly
  1724. * by not executing and remaining disabled, ensuring safe handling of
  1725. * degenerate iteration counts.
  1726. *
  1727. * ZERO ITERATIONS BEHAVIOR:
  1728. * - Task with 0 iterations should never execute
  1729. * - Task should be automatically disabled (no valid executions)
  1730. * - Scheduler should safely handle such tasks without errors
  1731. * - Useful for conditional task creation patterns
  1732. *
  1733. * TEST SCENARIO:
  1734. * 1. Create task with 0 iterations but enabled=true
  1735. * 2. Run scheduler multiple times
  1736. * 3. Verify no executions occur
  1737. * 4. Verify task becomes/remains disabled
  1738. *
  1739. * EXPECTATIONS:
  1740. * - Callback counter remains 0 (no executions)
  1741. * - Task is disabled (invalid iteration count)
  1742. * - No errors or crashes occur
  1743. *
  1744. * IMPORTANCE: Zero iteration handling prevents invalid execution
  1745. * states and enables conditional task creation patterns, essential
  1746. * for robust edge case handling.
  1747. */
  1748. TEST_F(SchedulerThoroughTest, TaskZeroIterations) {
  1749. Scheduler ts;
  1750. Task task(100, 0, &basic_callback, &ts, true);
  1751. // Should not execute with 0 iterations
  1752. delay(200);
  1753. ts.execute();
  1754. EXPECT_EQ(callback_counter, 0);
  1755. EXPECT_FALSE(task.isEnabled()); // Should be disabled
  1756. }
  1757. /**
  1758. * @brief Edge case test for task with infinite iterations (TASK_FOREVER)
  1759. *
  1760. * TESTS: Task behavior with TASK_FOREVER iterations
  1761. *
  1762. * PURPOSE: Verify that tasks configured with TASK_FOREVER execute
  1763. * indefinitely without auto-disabling, maintaining consistent behavior
  1764. * for infinite execution scenarios.
  1765. *
  1766. * INFINITE ITERATIONS BEHAVIOR:
  1767. * - TASK_FOREVER (-1) indicates infinite iterations
  1768. * - Task never auto-disables due to iteration count
  1769. * - getIterations() continues returning TASK_FOREVER
  1770. * - Executions continue until manually disabled
  1771. * - Essential for background and monitoring tasks
  1772. *
  1773. * TEST SCENARIO:
  1774. * 1. Create task with TASK_FOREVER iterations
  1775. * 2. Run scheduler until multiple executions occur
  1776. * 3. Verify task remains enabled throughout
  1777. * 4. Verify getIterations() still returns TASK_FOREVER
  1778. *
  1779. * EXPECTATIONS:
  1780. * - Task executes at least 5 times within timeout
  1781. * - Task remains enabled after multiple executions
  1782. * - getIterations() continues returning TASK_FOREVER
  1783. * - No auto-disable occurs
  1784. *
  1785. * IMPORTANCE: Infinite iteration support enables background tasks,
  1786. * monitoring loops, and continuous processing, essential for
  1787. * long-running and service-oriented applications.
  1788. */
  1789. TEST_F(SchedulerThoroughTest, TaskInfiniteIterations) {
  1790. Scheduler ts;
  1791. Task task(50, TASK_FOREVER, &basic_callback, &ts, true);
  1792. // Should keep running indefinitely
  1793. bool success = runSchedulerUntil(ts, []() { return callback_counter >= 5; }, 400);
  1794. EXPECT_TRUE(success);
  1795. EXPECT_TRUE(task.isEnabled()); // Should still be enabled
  1796. EXPECT_EQ(task.getIterations(), TASK_FOREVER); // Should remain -1
  1797. }
  1798. /**
  1799. * @brief Edge case test for task with null callback pointer
  1800. *
  1801. * TESTS: Task behavior with nullptr callback
  1802. *
  1803. * PURPOSE: Verify that tasks created with null callback pointers handle
  1804. * execution gracefully without crashing, demonstrating robustness in
  1805. * edge cases and enabling placeholder task patterns.
  1806. *
  1807. * NULL CALLBACK BEHAVIOR:
  1808. * - Task with nullptr callback should not crash during execution
  1809. * - Task lifecycle continues normally (timing, iterations, etc.)
  1810. * - No callback code executes (safe no-op behavior)
  1811. * - Enables placeholder and template task patterns
  1812. * - Demonstrates scheduler robustness
  1813. *
  1814. * TEST SCENARIO:
  1815. * 1. Create task with nullptr callback but valid parameters
  1816. * 2. Run scheduler and let task execute
  1817. * 3. Verify no crashes or exceptions occur
  1818. * 4. Verify no callback-specific effects occur
  1819. *
  1820. * EXPECTATIONS:
  1821. * - No crashes or exceptions during execution
  1822. * - Callback counter remains 0 (no callback executed)
  1823. * - Task lifecycle proceeds normally
  1824. * - Scheduler continues operating safely
  1825. *
  1826. * IMPORTANCE: Null callback handling ensures scheduler robustness
  1827. * and enables placeholder task patterns, essential for template
  1828. * systems and defensive programming practices.
  1829. */
  1830. TEST_F(SchedulerThoroughTest, TaskNullCallback) {
  1831. Scheduler ts;
  1832. Task task(100, 3, nullptr, &ts, true);
  1833. // Should not crash with null callback
  1834. delay(200);
  1835. ts.execute();
  1836. EXPECT_EQ(callback_counter, 0); // No callback executed
  1837. // Task should still run through its lifecycle
  1838. }
  1839. /**
  1840. * @brief Main test runner function
  1841. *
  1842. * Initializes Google Test framework and runs all registered test cases.
  1843. * Called by the test execution environment to start the testing process.
  1844. * Returns 0 for success, non-zero for test failures.
  1845. *
  1846. * @param argc Command line argument count
  1847. * @param argv Command line argument values
  1848. * @return Test execution status (0 = success)
  1849. */
  1850. int main(int argc, char **argv) {
  1851. ::testing::InitGoogleTest(&argc, argv);
  1852. return RUN_ALL_TESTS();
  1853. }