test-scheduler-basic-thorough.cpp 79 KB

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