test-scheduler-priority.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. // test-scheduler-priority.cpp - TaskScheduler layered prioritization unit tests
  2. // This file contains comprehensive tests for TaskScheduler layered prioritization
  3. // functionality enabled by the _TASK_PRIORITY compile-time directive
  4. //
  5. // =====================================================================================
  6. // PRIORITY TEST PLAN AND COVERAGE MATRIX
  7. // =====================================================================================
  8. //
  9. // PURPOSE: Validate TaskScheduler layered prioritization system
  10. // APPROACH: Traditional function pointers for maximum platform compatibility
  11. // SCOPE: Priority scheduling, scheduler hierarchy, and execution ordering
  12. //
  13. // COMPILE DIRECTIVES TESTED:
  14. // - _TASK_PRIORITY: Layered task prioritization support
  15. //
  16. // COVERAGE MATRIX:
  17. // ================
  18. //
  19. // 1. BASIC PRIORITY FUNCTIONALITY
  20. // ├── Scheduler Creation and Hierarchy Setup
  21. // │ ├── Base scheduler creation
  22. // │ ├── High priority scheduler creation
  23. // │ ├── setHighPriorityScheduler() method
  24. // │ ├── currentScheduler() method
  25. // │ └── Scheduler hierarchy validation
  26. // │
  27. // ├── Task Assignment to Priority Levels
  28. // │ ├── Tasks added to base priority scheduler
  29. // │ ├── Tasks added to high priority scheduler
  30. // │ ├── Task execution order validation
  31. // │ └── Priority-based scheduling behavior
  32. // │
  33. // └── Basic Priority Execution Patterns
  34. // ├── High priority tasks execute before base priority
  35. // ├── Multiple high priority tasks execution order
  36. // ├── Base priority tasks execution after high priority
  37. // └── Mixed priority execution scenarios
  38. //
  39. // 2. MULTI-LAYER PRIORITY HIERARCHY
  40. // ├── Three-Layer Priority System
  41. // │ ├── Base priority (lowest)
  42. // │ ├── Medium priority (middle)
  43. // │ ├── High priority (highest)
  44. // │ └── Execution order verification
  45. // │
  46. // ├── Complex Priority Chain Execution
  47. // │ ├── Nested scheduler execute() calls
  48. // │ ├── Priority evaluation sequence
  49. // │ ├── Scheduling overhead measurement
  50. // │ └── Performance impact validation
  51. // │
  52. // └── Priority Collision Handling
  53. // ├── Same-time scheduling of different priorities
  54. // ├── Priority-based task selection
  55. // ├── Execution sequence preservation
  56. // └── Timing accuracy in priority scenarios
  57. //
  58. // 3. ADVANCED PRIORITY SCENARIOS
  59. // ├── Dynamic Priority Changes
  60. // │ ├── Runtime scheduler hierarchy modification
  61. // │ ├── Task migration between priority levels
  62. // │ ├── Priority scheduler enable/disable
  63. // │ └── Scheduler state management
  64. // │
  65. // ├── Priority with Task Features
  66. // │ ├── Priority + scheduling options integration
  67. // │ ├── Priority + status request coordination
  68. // │ ├── Priority + timeout handling
  69. // │ └── Priority + self-destruct behavior
  70. // │
  71. // └── Real-World Priority Use Cases
  72. // ├── Time-critical sensor reading (high priority)
  73. // ├── Background data processing (base priority)
  74. // ├── Emergency response handling (highest priority)
  75. // └── Resource-intensive calculations (low priority)
  76. //
  77. // 4. PRIORITY PERFORMANCE AND OVERHEAD
  78. // ├── Scheduling Overhead Analysis
  79. // │ ├── Single scheduler vs. layered performance
  80. // │ ├── Execution time measurement
  81. // │ ├── Priority evaluation cost
  82. // │ └── Overhead scaling with priority layers
  83. // │
  84. // ├── Timing Accuracy with Priorities
  85. // │ ├── High priority task timing precision
  86. // │ ├── Base priority task timing impact
  87. // │ ├── Priority-induced scheduling delays
  88. // │ └── Overall system timing accuracy
  89. // │
  90. // └── Resource Utilization
  91. // ├── Memory usage with priority layers
  92. // ├── CPU utilization patterns
  93. // ├── Stack depth analysis
  94. // └── Performance optimization guidelines
  95. //
  96. // =====================================================================================
  97. // Enable priority functionality for comprehensive testing
  98. #define _TASK_PRIORITY // Layered task prioritization
  99. #define _TASK_WDT_IDS
  100. #define _TASK_TIMECRITICAL
  101. #include <gtest/gtest.h>
  102. #include "Arduino.h"
  103. #include "TaskScheduler.h"
  104. // Global test state for priority testing
  105. std::vector<std::string> priority_test_output;
  106. int priority_callback_counter = 0;
  107. unsigned long priority_execution_times[20]; // Store execution timestamps
  108. int priority_execution_index = 0;
  109. // Definition for test_output vector used by Arduino.h mock
  110. std::vector<std::string> test_output;
  111. // Test callback functions (no lambda functions)
  112. /**
  113. * @brief Base priority task callback - simulates normal priority work
  114. */
  115. void base_priority_callback() {
  116. priority_callback_counter++;
  117. priority_test_output.push_back("base_priority_executed");
  118. priority_execution_times[priority_execution_index++] = millis();
  119. std::cout << "Base priority task executed at " << millis() << "ms" << std::endl;
  120. }
  121. /**
  122. * @brief High priority task callback - simulates critical priority work
  123. */
  124. void high_priority_callback() {
  125. priority_callback_counter++;
  126. priority_test_output.push_back("high_priority_executed");
  127. priority_execution_times[priority_execution_index++] = millis();
  128. std::cout << "High priority task executed at " << millis() << "ms" << std::endl;
  129. }
  130. /**
  131. * @brief Priority test callback that uses currentScheduler() and currentTask()
  132. *
  133. * This callback mimics the example11 pattern by accessing the current scheduler
  134. * and task to get task ID and timing information.
  135. */
  136. void priority_test_callback() {
  137. priority_callback_counter++;
  138. // Use currentScheduler() and currentTask() like in example11
  139. Scheduler& current_scheduler = Scheduler::currentScheduler();
  140. Task& current_task = current_scheduler.currentTask();
  141. // Record task execution with ID for verification
  142. unsigned int task_id = current_task.getId();
  143. unsigned long execution_time = millis();
  144. priority_execution_times[priority_execution_index++] = execution_time;
  145. // Create output string with task ID for verification
  146. std::string output = "task_" + std::to_string(task_id) + "_executed";
  147. priority_test_output.push_back(output);
  148. std::cout << "Task: " << task_id << " executed at " << execution_time
  149. << "ms, Start delay = " << current_task.getStartDelay() << std::endl;
  150. }
  151. /**
  152. * @brief Medium priority task callback - simulates intermediate priority work
  153. */
  154. void medium_priority_callback() {
  155. priority_callback_counter++;
  156. priority_test_output.push_back("medium_priority_executed");
  157. priority_execution_times[priority_execution_index++] = millis();
  158. std::cout << "Medium priority task executed at " << millis() << "ms" << std::endl;
  159. }
  160. /**
  161. * @brief Time-critical sensor callback - simulates gyroscope/accelerometer reading
  162. */
  163. void sensor_critical_callback() {
  164. priority_callback_counter++;
  165. priority_test_output.push_back("sensor_critical_executed");
  166. priority_execution_times[priority_execution_index++] = millis();
  167. // Simulate precise sensor reading requiring minimal delay
  168. }
  169. /**
  170. * @brief Background processing callback - simulates non-critical background work
  171. */
  172. void background_processing_callback() {
  173. priority_callback_counter++;
  174. priority_test_output.push_back("background_processing_executed");
  175. priority_execution_times[priority_execution_index++] = millis();
  176. // Simulate background data processing
  177. }
  178. /**
  179. * @brief Emergency response callback - simulates highest priority emergency handling
  180. */
  181. void emergency_response_callback() {
  182. priority_callback_counter++;
  183. priority_test_output.push_back("emergency_response_executed");
  184. priority_execution_times[priority_execution_index++] = millis();
  185. // Simulate emergency response requiring immediate attention
  186. }
  187. /**
  188. * @brief Test fixture class for TaskScheduler priority functionality
  189. *
  190. * Provides setup and teardown for priority tests, ensuring clean state
  191. * between tests and utility methods for priority scenario validation.
  192. */
  193. class PrioritySchedulerTest : public ::testing::Test {
  194. protected:
  195. /**
  196. * @brief Set up test environment for priority testing
  197. *
  198. * Clears all test state and initializes timing system for priority tests.
  199. */
  200. void SetUp() override {
  201. clearPriorityTestOutput();
  202. priority_callback_counter = 0;
  203. priority_execution_index = 0;
  204. memset(priority_execution_times, 0, sizeof(priority_execution_times));
  205. millis(); // Initialize timing
  206. }
  207. /**
  208. * @brief Clean up test environment after priority tests
  209. *
  210. * Ensures no test artifacts affect subsequent tests.
  211. */
  212. void TearDown() override {
  213. clearPriorityTestOutput();
  214. priority_callback_counter = 0;
  215. priority_execution_index = 0;
  216. memset(priority_execution_times, 0, sizeof(priority_execution_times));
  217. }
  218. /**
  219. * @brief Helper to run scheduler until condition or timeout for priority tests
  220. */
  221. bool runPrioritySchedulerUntil(Scheduler& ts, std::function<bool()> condition, unsigned long timeout_ms = 2000) {
  222. return waitForCondition([&]() {
  223. ts.execute();
  224. return condition();
  225. }, timeout_ms);
  226. }
  227. /**
  228. * @brief Clear priority test output buffer
  229. */
  230. void clearPriorityTestOutput() {
  231. priority_test_output.clear();
  232. }
  233. /**
  234. * @brief Get count of priority test output entries
  235. */
  236. size_t getPriorityTestOutputCount() {
  237. return priority_test_output.size();
  238. }
  239. /**
  240. * @brief Get specific priority test output entry
  241. */
  242. std::string getPriorityTestOutput(size_t index) {
  243. if (index < priority_test_output.size()) {
  244. return priority_test_output[index];
  245. }
  246. return "";
  247. }
  248. /**
  249. * @brief Verify execution order matches expected priority sequence
  250. */
  251. bool verifyExecutionOrder(const std::vector<std::string>& expected_order) {
  252. if (expected_order.size() != priority_test_output.size()) {
  253. return false;
  254. }
  255. for (size_t i = 0; i < expected_order.size(); i++) {
  256. if (expected_order[i] != priority_test_output[i]) {
  257. return false;
  258. }
  259. }
  260. return true;
  261. }
  262. /**
  263. * @brief Analyze priority evaluation pattern based on example11 sequence
  264. *
  265. * According to wiki: "entire chain of tasks of the higher priority scheduler
  266. * is executed for every single step (task) of the lower priority chain"
  267. *
  268. * Expected pattern: high priority tasks are evaluated between each base task
  269. */
  270. bool validatePriorityEvaluationPattern() {
  271. // Look for the pattern where high priority tasks (4,5) are frequently interspersed
  272. // with base priority tasks (1,2,3), especially task 4 which has shortest interval
  273. int consecutive_base_tasks = 0;
  274. int max_consecutive_base = 0;
  275. for (size_t i = 0; i < getPriorityTestOutputCount(); i++) {
  276. std::string task = getPriorityTestOutput(i);
  277. if (task == "task_1_executed" || task == "task_2_executed" || task == "task_3_executed") {
  278. consecutive_base_tasks++;
  279. max_consecutive_base = std::max(max_consecutive_base, consecutive_base_tasks);
  280. } else {
  281. consecutive_base_tasks = 0;
  282. }
  283. }
  284. // In proper priority scheduling, we shouldn't see many consecutive base tasks
  285. // because high priority tasks should be evaluated frequently
  286. return max_consecutive_base <= 3; // Allow some consecutive base tasks but not too many
  287. }
  288. };
  289. // ================== BASIC PRIORITY FUNCTIONALITY TESTS ==================
  290. /**
  291. * @brief Test basic scheduler hierarchy setup and validation (based on example11)
  292. *
  293. * TESTS: setHighPriorityScheduler(), currentScheduler(), currentTask()
  294. *
  295. * PURPOSE: Verify that scheduler hierarchy can be established correctly
  296. * and that the priority relationships work as expected for basic scenarios,
  297. * following the pattern from Scheduler_example11_Priority.
  298. *
  299. * PRIORITY HIERARCHY SETUP (from example11):
  300. * - Base scheduler: Tasks t1, t2, t3 (1000ms, 2000ms, 3000ms intervals)
  301. * - High scheduler: Tasks t4, t5 (500ms, 1000ms intervals)
  302. * - Hierarchy relationship: base.setHighPriorityScheduler(&high)
  303. * - Execution order follows priority evaluation sequence: 4,5,1,4,5,2,4,5,3
  304. *
  305. * TEST SCENARIO:
  306. * 1. Create base and high priority schedulers matching example11
  307. * 2. Establish hierarchy relationship
  308. * 3. Add tasks to both schedulers with example11 intervals
  309. * 4. Execute base scheduler and verify priority execution pattern
  310. * 5. Verify currentScheduler() and currentTask() work correctly
  311. *
  312. * EXPECTATIONS:
  313. * - High priority tasks (t4, t5) execute more frequently
  314. * - currentScheduler() returns correct scheduler during execution
  315. * - Task IDs can be retrieved and verified
  316. * - Priority evaluation follows documented sequence
  317. */
  318. TEST_F(PrioritySchedulerTest, BasicSchedulerHierarchy) {
  319. // Create base and high priority schedulers (matching example11)
  320. Scheduler base_scheduler;
  321. Scheduler high_scheduler;
  322. // Establish hierarchy - high scheduler has priority over base
  323. base_scheduler.setHighPriorityScheduler(&high_scheduler);
  324. // Add tasks to schedulers with intervals matching example11
  325. // Base priority tasks: longer intervals
  326. Task t1(1000, 3, &priority_test_callback, &base_scheduler, false); // 1000ms interval
  327. Task t2(2000, 2, &priority_test_callback, &base_scheduler, false); // 2000ms interval
  328. Task t3(3000, 1, &priority_test_callback, &base_scheduler, false); // 3000ms interval
  329. // High priority tasks: shorter intervals for more frequent execution
  330. Task t4(500, 6, &priority_test_callback, &high_scheduler, false); // 500ms interval
  331. Task t5(1000, 3, &priority_test_callback, &high_scheduler, false); // 1000ms interval
  332. // Set task IDs for identification (like in example11)
  333. t1.setId(1);
  334. t2.setId(2);
  335. t3.setId(3);
  336. t4.setId(4);
  337. t5.setId(5);
  338. // Enable all tasks recursively (like example11: enableAll(true))
  339. base_scheduler.enableAll(true);
  340. // Verify tasks are enabled
  341. EXPECT_TRUE(t1.isEnabled());
  342. EXPECT_TRUE(t2.isEnabled());
  343. EXPECT_TRUE(t3.isEnabled());
  344. EXPECT_TRUE(t4.isEnabled());
  345. EXPECT_TRUE(t5.isEnabled());
  346. // Execute scheduler for sufficient time to see priority pattern
  347. // High priority tasks should execute more frequently due to shorter intervals
  348. bool success = runPrioritySchedulerUntil(base_scheduler, []() {
  349. return priority_callback_counter >= 15; // 6+3=9 high + 3+2+1=6 base = 15 total
  350. }, 5000);
  351. EXPECT_TRUE(success);
  352. EXPECT_EQ(priority_callback_counter, 15);
  353. // Count executions by task ID to verify the priority pattern
  354. int task1_count = 0, task2_count = 0, task3_count = 0; // Base priority tasks
  355. int task4_count = 0, task5_count = 0; // High priority tasks
  356. for (size_t i = 0; i < getPriorityTestOutputCount(); i++) {
  357. std::string output = getPriorityTestOutput(i);
  358. if (output == "task_1_executed") task1_count++;
  359. else if (output == "task_2_executed") task2_count++;
  360. else if (output == "task_3_executed") task3_count++;
  361. else if (output == "task_4_executed") task4_count++;
  362. else if (output == "task_5_executed") task5_count++;
  363. }
  364. // Verify execution counts match expected iterations
  365. EXPECT_EQ(task1_count, 3); // t1: 3 executions (1000ms interval)
  366. EXPECT_EQ(task2_count, 2); // t2: 2 executions (2000ms interval)
  367. EXPECT_EQ(task3_count, 1); // t3: 1 execution (3000ms interval)
  368. EXPECT_EQ(task4_count, 6); // t4: 6 executions (500ms interval, high priority)
  369. EXPECT_EQ(task5_count, 3); // t5: 3 executions (1000ms interval, high priority)
  370. // Verify that task 4 (highest frequency, high priority) executed first
  371. // Task 4 has 500ms interval and high priority, so it should execute first
  372. EXPECT_EQ(getPriorityTestOutput(0), "task_4_executed");
  373. // Count high vs base priority executions for overall pattern verification
  374. int high_priority_total = task4_count + task5_count; // 6 + 3 = 9
  375. int base_priority_total = task1_count + task2_count + task3_count; // 3 + 2 + 1 = 6
  376. EXPECT_EQ(high_priority_total, 9);
  377. EXPECT_EQ(base_priority_total, 6);
  378. // Validate the priority execution order pattern from example11 output
  379. // Expected initial sequence: 4, 5, 1, 2, 3 (based on timing and priority)
  380. // Task 4 (ID 40 in example) executes at 0ms, 500ms, 1000ms, 1500ms, 2000ms, 2500ms
  381. // Task 5 (ID 50 in example) executes at 10ms, 1010ms, 2011ms, 3010ms, etc.
  382. // Task 1 executes at ~21ms, 1021ms, 2022ms, 3021ms, etc.
  383. // Verify high priority tasks execute more frequently in early execution
  384. std::vector<std::string> early_executions;
  385. for (size_t i = 0; i < std::min((size_t)10, getPriorityTestOutputCount()); i++) {
  386. early_executions.push_back(getPriorityTestOutput(i));
  387. }
  388. // Count high priority vs base priority in first 10 executions
  389. int early_high_count = 0;
  390. int early_base_count = 0;
  391. for (const auto& exec : early_executions) {
  392. if (exec == "task_4_executed" || exec == "task_5_executed") {
  393. early_high_count++;
  394. } else if (exec == "task_1_executed" || exec == "task_2_executed" || exec == "task_3_executed") {
  395. early_base_count++;
  396. }
  397. }
  398. // High priority tasks should dominate early execution due to shorter intervals
  399. EXPECT_GE(early_high_count, early_base_count);
  400. // Verify task 4 appears multiple times in early execution (due to 500ms interval)
  401. int task4_early_count = 0;
  402. for (const auto& exec : early_executions) {
  403. if (exec == "task_4_executed") {
  404. task4_early_count++;
  405. }
  406. }
  407. EXPECT_GE(task4_early_count, 2); // Task 4 should execute at least twice in early sequence
  408. // Validate that base priority tasks eventually execute
  409. // Task 1 should appear at least once given its 1000ms interval
  410. bool task1_found = false;
  411. bool task2_found = (task2_count > 0); // Task 2 might not execute in early sequence due to 2000ms interval
  412. for (const auto& exec : priority_test_output) {
  413. if (exec == "task_1_executed") {
  414. task1_found = true;
  415. break;
  416. }
  417. }
  418. EXPECT_TRUE(task1_found);
  419. // Print execution sequence for debugging (similar to example11 output)
  420. std::cout << "\nPriority Test Execution Sequence:" << std::endl;
  421. for (size_t i = 0; i < getPriorityTestOutputCount() && i < 15; i++) {
  422. std::string task_id = getPriorityTestOutput(i);
  423. // Extract task number from "task_X_executed"
  424. if (task_id.length() > 5) {
  425. char task_num = task_id[5];
  426. std::cout << "Task: " << task_num << " at position " << i << std::endl;
  427. }
  428. }
  429. // Validate the specific execution order pattern from example11 output
  430. // This ensures the priority evaluation sequence matches documented behavior
  431. EXPECT_TRUE(validatePriorityEvaluationPattern())
  432. << "Priority evaluation pattern validation failed - high priority tasks should be interspersed with base tasks";
  433. }
  434. /**
  435. * @brief Test two-layer priority scheduling with timing validation
  436. *
  437. * TESTS: Priority-based execution timing and sequence
  438. *
  439. * PURPOSE: Verify that two-layer priority scheduling works correctly
  440. * with proper timing relationships and execution sequence preservation.
  441. *
  442. * PRIORITY EXECUTION PATTERN:
  443. * According to wiki documentation:
  444. * "Task prioritization is achieved by executing the entire chain of tasks
  445. * of the higher priority scheduler for every single step (task) of the
  446. * lower priority chain."
  447. *
  448. * For 5 base tasks and 2 high priority tasks:
  449. * Execution sequence: H1 -> H2 -> B1 -> H1 -> H2 -> B2 -> H1 -> H2 -> B3 -> H1 -> H2 -> B4 -> H1 -> H2 -> B5
  450. *
  451. * TEST SCENARIO:
  452. * 1. Set up base scheduler with 5 tasks at 100ms intervals
  453. * 2. Set up high scheduler with 2 tasks at 50ms intervals
  454. * 3. Run for sufficient time to see priority pattern
  455. * 4. Verify high priority tasks get evaluated more frequently
  456. * 5. Validate timing relationships
  457. */
  458. TEST_F(PrioritySchedulerTest, TwoLayerPriorityTiming) {
  459. Scheduler base_scheduler;
  460. Scheduler high_scheduler;
  461. base_scheduler.setHighPriorityScheduler(&high_scheduler);
  462. // Base priority tasks - longer intervals
  463. Task base_task1(200, 3, &base_priority_callback, &base_scheduler, true);
  464. // High priority tasks - shorter intervals for more frequent execution
  465. Task high_task1(50, 6, &high_priority_callback, &high_scheduler, true);
  466. // Run scheduler and measure execution patterns
  467. unsigned long start_time = millis();
  468. bool success = runPrioritySchedulerUntil(base_scheduler, []() {
  469. return priority_callback_counter >= 9; // 6 high + 3 base = 9 total
  470. }, 3000);
  471. EXPECT_TRUE(success);
  472. EXPECT_EQ(priority_callback_counter, 9);
  473. // Verify that high priority tasks executed more frequently
  474. int high_priority_count = 0;
  475. int base_priority_count = 0;
  476. for (size_t i = 0; i < getPriorityTestOutputCount(); i++) {
  477. if (getPriorityTestOutput(i) == "high_priority_executed") {
  478. high_priority_count++;
  479. } else if (getPriorityTestOutput(i) == "base_priority_executed") {
  480. base_priority_count++;
  481. }
  482. }
  483. EXPECT_EQ(high_priority_count, 6);
  484. EXPECT_EQ(base_priority_count, 3);
  485. // High priority tasks should execute first due to shorter interval
  486. EXPECT_EQ(getPriorityTestOutput(0), "high_priority_executed");
  487. }
  488. /**
  489. * @brief Test priority collision handling - tasks scheduled at same time
  490. *
  491. * TESTS: Priority-based task selection when multiple tasks are ready
  492. *
  493. * PURPOSE: Verify that when multiple tasks from different priority levels
  494. * are ready to execute simultaneously, the priority system correctly
  495. * selects high priority tasks first.
  496. */
  497. TEST_F(PrioritySchedulerTest, PriorityCollisionHandling) {
  498. Scheduler base_scheduler;
  499. Scheduler high_scheduler;
  500. base_scheduler.setHighPriorityScheduler(&high_scheduler);
  501. // Set tasks to execute at similar times to create scheduling collision
  502. Task base_task(100, 1, &base_priority_callback, &base_scheduler, false);
  503. Task high_task(100, 1, &high_priority_callback, &high_scheduler, false);
  504. // Enable both tasks at the same time
  505. base_task.enable();
  506. high_task.enable();
  507. // Execute scheduler - both tasks are ready immediately
  508. bool success = runPrioritySchedulerUntil(base_scheduler, []() {
  509. return priority_callback_counter >= 2;
  510. });
  511. EXPECT_TRUE(success);
  512. EXPECT_EQ(priority_callback_counter, 2);
  513. // High priority task should execute first in collision scenario
  514. EXPECT_EQ(getPriorityTestOutput(0), "high_priority_executed");
  515. EXPECT_EQ(getPriorityTestOutput(1), "base_priority_executed");
  516. }
  517. // ================== MULTI-LAYER PRIORITY HIERARCHY TESTS ==================
  518. /**
  519. * @brief Test three-layer priority hierarchy
  520. *
  521. * TESTS: Complex multi-layer priority scheduling
  522. *
  523. * PURPOSE: Verify that three-layer priority hierarchy works correctly
  524. * with proper execution order: Emergency -> High -> Base priority.
  525. */
  526. TEST_F(PrioritySchedulerTest, ThreeLayerPriorityHierarchy) {
  527. Scheduler base_scheduler;
  528. Scheduler high_scheduler;
  529. Scheduler emergency_scheduler;
  530. // Establish three-layer hierarchy
  531. // Emergency (highest) -> High -> Base (lowest)
  532. high_scheduler.setHighPriorityScheduler(&emergency_scheduler);
  533. base_scheduler.setHighPriorityScheduler(&high_scheduler);
  534. // Add tasks to each priority level
  535. Task base_task(100, 2, &base_priority_callback, &base_scheduler, true);
  536. Task high_task(100, 2, &high_priority_callback, &high_scheduler, true);
  537. Task emergency_task(100, 1, &emergency_response_callback, &emergency_scheduler, true);
  538. // Execute base scheduler - should process all layers in priority order
  539. bool success = runPrioritySchedulerUntil(base_scheduler, []() {
  540. return priority_callback_counter >= 5; // 1 emergency + 2 high + 2 base = 5
  541. });
  542. EXPECT_TRUE(success);
  543. EXPECT_EQ(priority_callback_counter, 5);
  544. // Verify execution order follows three-layer priority
  545. EXPECT_EQ(getPriorityTestOutput(0), "emergency_response_executed");
  546. // High priority tasks should execute next
  547. bool found_high_after_emergency = false;
  548. for (size_t i = 1; i < getPriorityTestOutputCount(); i++) {
  549. if (getPriorityTestOutput(i) == "high_priority_executed") {
  550. found_high_after_emergency = true;
  551. break;
  552. }
  553. }
  554. EXPECT_TRUE(found_high_after_emergency);
  555. }
  556. /**
  557. * @brief Test priority scheduling overhead measurement
  558. *
  559. * TESTS: Performance impact of layered priority scheduling
  560. *
  561. * PURPOSE: Validate that priority scheduling overhead is reasonable
  562. * and measure the performance impact of different priority configurations.
  563. *
  564. * According to wiki: "Scheduling overhead of a 3 layer prioritization
  565. * approach is 3 times higher than that of a flat execution chain."
  566. */
  567. TEST_F(PrioritySchedulerTest, PrioritySchedulingOverhead) {
  568. // Test 1: Single scheduler (baseline)
  569. {
  570. clearPriorityTestOutput();
  571. priority_callback_counter = 0;
  572. Scheduler single_scheduler;
  573. Task task1(50, 10, &base_priority_callback, &single_scheduler, true);
  574. unsigned long start_time = millis();
  575. runPrioritySchedulerUntil(single_scheduler, []() {
  576. return priority_callback_counter >= 10;
  577. });
  578. unsigned long single_scheduler_time = millis() - start_time;
  579. EXPECT_EQ(priority_callback_counter, 10);
  580. std::cout << "Single scheduler time: " << single_scheduler_time << "ms" << std::endl;
  581. }
  582. // Test 2: Two-layer priority scheduler
  583. {
  584. clearPriorityTestOutput();
  585. priority_callback_counter = 0;
  586. Scheduler base_scheduler;
  587. Scheduler high_scheduler;
  588. base_scheduler.setHighPriorityScheduler(&high_scheduler);
  589. Task base_task(50, 5, &base_priority_callback, &base_scheduler, true);
  590. Task high_task(50, 5, &high_priority_callback, &high_scheduler, true);
  591. unsigned long start_time = millis();
  592. runPrioritySchedulerUntil(base_scheduler, []() {
  593. return priority_callback_counter >= 10;
  594. });
  595. unsigned long priority_scheduler_time = millis() - start_time;
  596. EXPECT_EQ(priority_callback_counter, 10);
  597. std::cout << "Priority scheduler time: " << priority_scheduler_time << "ms" << std::endl;
  598. // Priority scheduling should be slower but still reasonable
  599. // We don't enforce strict timing due to test environment variability
  600. EXPECT_GT(priority_scheduler_time, 0);
  601. }
  602. }
  603. // ================== ADVANCED PRIORITY SCENARIOS ==================
  604. /**
  605. * @brief Test real-world priority use case - sensor + background processing
  606. *
  607. * TESTS: Realistic priority scenario with time-critical and background tasks
  608. *
  609. * PURPOSE: Validate priority system works for real-world scenarios like
  610. * time-critical sensor reading with background data processing.
  611. *
  612. * SCENARIO:
  613. * - High priority: Time-critical sensor reading (gyroscope/accelerometer)
  614. * - Base priority: Background data processing and housekeeping
  615. * - Emergency priority: Safety monitoring and emergency response
  616. */
  617. TEST_F(PrioritySchedulerTest, RealWorldSensorPriorityScenario) {
  618. Scheduler base_scheduler;
  619. Scheduler sensor_scheduler;
  620. Scheduler emergency_scheduler;
  621. // Set up three-layer priority: Emergency > Sensor > Background
  622. sensor_scheduler.setHighPriorityScheduler(&emergency_scheduler);
  623. base_scheduler.setHighPriorityScheduler(&sensor_scheduler);
  624. // Background processing - base priority, longer intervals
  625. Task background_task(500, 2, &background_processing_callback, &base_scheduler, true);
  626. // Time-critical sensor reading - high priority, short intervals
  627. Task sensor_task(10, 10, &sensor_critical_callback, &sensor_scheduler, true);
  628. // Emergency monitoring - highest priority, triggered as needed
  629. Task emergency_task(1000, 1, &emergency_response_callback, &emergency_scheduler, true);
  630. // Run scenario and verify sensor tasks get priority
  631. bool success = runPrioritySchedulerUntil(base_scheduler, []() {
  632. return priority_callback_counter >= 13; // 1 emergency + 10 sensor + 2 background
  633. }, 3000);
  634. EXPECT_TRUE(success);
  635. EXPECT_EQ(priority_callback_counter, 13);
  636. // Count executions by priority level
  637. int emergency_count = 0;
  638. int sensor_count = 0;
  639. int background_count = 0;
  640. for (size_t i = 0; i < getPriorityTestOutputCount(); i++) {
  641. std::string output = getPriorityTestOutput(i);
  642. if (output == "emergency_response_executed") {
  643. emergency_count++;
  644. } else if (output == "sensor_critical_executed") {
  645. sensor_count++;
  646. } else if (output == "background_processing_executed") {
  647. background_count++;
  648. }
  649. }
  650. EXPECT_EQ(emergency_count, 1);
  651. EXPECT_EQ(sensor_count, 10);
  652. EXPECT_EQ(background_count, 2);
  653. // Emergency should execute first if present
  654. EXPECT_EQ(getPriorityTestOutput(0), "emergency_response_executed");
  655. }
  656. /**
  657. * @brief Test dynamic priority scheduler changes
  658. *
  659. * TESTS: Runtime modification of scheduler hierarchy
  660. *
  661. * PURPOSE: Verify that priority relationships can be changed at runtime
  662. * and that the system adapts correctly to new priority configurations.
  663. */
  664. TEST_F(PrioritySchedulerTest, DynamicPriorityChanges) {
  665. Scheduler base_scheduler;
  666. Scheduler high_scheduler;
  667. Scheduler alternative_scheduler;
  668. // Initial setup: base -> high priority
  669. base_scheduler.setHighPriorityScheduler(&high_scheduler);
  670. Task base_task(100, 1, &base_priority_callback, &base_scheduler, true);
  671. Task high_task(100, 1, &high_priority_callback, &high_scheduler, true);
  672. Task alt_task(100, 1, &medium_priority_callback, &alternative_scheduler, true);
  673. // Execute with initial hierarchy
  674. bool success = runPrioritySchedulerUntil(base_scheduler, []() {
  675. return priority_callback_counter >= 2;
  676. });
  677. EXPECT_TRUE(success);
  678. EXPECT_EQ(priority_callback_counter, 2);
  679. EXPECT_EQ(getPriorityTestOutput(0), "high_priority_executed");
  680. EXPECT_EQ(getPriorityTestOutput(1), "base_priority_executed");
  681. // Change priority hierarchy at runtime
  682. clearPriorityTestOutput();
  683. priority_callback_counter = 0;
  684. base_scheduler.setHighPriorityScheduler(&alternative_scheduler);
  685. // Re-enable tasks for another round
  686. base_task.restart();
  687. alt_task.restart();
  688. // Execute with new hierarchy
  689. success = runPrioritySchedulerUntil(base_scheduler, []() {
  690. return priority_callback_counter >= 2;
  691. });
  692. EXPECT_TRUE(success);
  693. EXPECT_EQ(priority_callback_counter, 2);
  694. EXPECT_EQ(getPriorityTestOutput(0), "medium_priority_executed");
  695. EXPECT_EQ(getPriorityTestOutput(1), "base_priority_executed");
  696. }
  697. /**
  698. * @brief Test priority system with enableAll/disableAll recursive operations
  699. *
  700. * TESTS: Recursive enable/disable operations across priority layers
  701. *
  702. * PURPOSE: Verify that enableAll(true) and disableAll(true) work correctly
  703. * with priority schedulers, affecting all layers in the hierarchy.
  704. */
  705. TEST_F(PrioritySchedulerTest, PriorityRecursiveEnableDisable) {
  706. Scheduler base_scheduler;
  707. Scheduler high_scheduler;
  708. base_scheduler.setHighPriorityScheduler(&high_scheduler);
  709. // Add tasks to both schedulers (initially disabled)
  710. Task base_task(100, 2, &base_priority_callback, &base_scheduler, false);
  711. Task high_task(100, 2, &high_priority_callback, &high_scheduler, false);
  712. // Verify tasks are initially disabled
  713. EXPECT_FALSE(base_task.isEnabled());
  714. EXPECT_FALSE(high_task.isEnabled());
  715. // Enable all tasks recursively (should enable high priority tasks too)
  716. base_scheduler.enableAll(true);
  717. EXPECT_TRUE(base_task.isEnabled());
  718. EXPECT_TRUE(high_task.isEnabled());
  719. // Execute to verify both schedulers work
  720. bool success = runPrioritySchedulerUntil(base_scheduler, []() {
  721. return priority_callback_counter >= 4;
  722. });
  723. EXPECT_TRUE(success);
  724. EXPECT_EQ(priority_callback_counter, 4);
  725. // Disable all tasks recursively
  726. base_scheduler.disableAll(true);
  727. EXPECT_FALSE(base_task.isEnabled());
  728. EXPECT_FALSE(high_task.isEnabled());
  729. }
  730. /**
  731. * @brief Main test runner function for priority functionality
  732. *
  733. * Initializes Google Test framework and runs all priority feature tests.
  734. * Called by the test execution environment to start testing process.
  735. */
  736. int main(int argc, char **argv) {
  737. ::testing::InitGoogleTest(&argc, argv);
  738. return RUN_ALL_TESTS();
  739. }