| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860 |
- // test-scheduler-priority.cpp - TaskScheduler layered prioritization unit tests
- // This file contains comprehensive tests for TaskScheduler layered prioritization
- // functionality enabled by the _TASK_PRIORITY compile-time directive
- //
- // =====================================================================================
- // PRIORITY TEST PLAN AND COVERAGE MATRIX
- // =====================================================================================
- //
- // PURPOSE: Validate TaskScheduler layered prioritization system
- // APPROACH: Traditional function pointers for maximum platform compatibility
- // SCOPE: Priority scheduling, scheduler hierarchy, and execution ordering
- //
- // COMPILE DIRECTIVES TESTED:
- // - _TASK_PRIORITY: Layered task prioritization support
- //
- // COVERAGE MATRIX:
- // ================
- //
- // 1. BASIC PRIORITY FUNCTIONALITY
- // ├── Scheduler Creation and Hierarchy Setup
- // │ ├── Base scheduler creation
- // │ ├── High priority scheduler creation
- // │ ├── setHighPriorityScheduler() method
- // │ ├── currentScheduler() method
- // │ └── Scheduler hierarchy validation
- // │
- // ├── Task Assignment to Priority Levels
- // │ ├── Tasks added to base priority scheduler
- // │ ├── Tasks added to high priority scheduler
- // │ ├── Task execution order validation
- // │ └── Priority-based scheduling behavior
- // │
- // └── Basic Priority Execution Patterns
- // ├── High priority tasks execute before base priority
- // ├── Multiple high priority tasks execution order
- // ├── Base priority tasks execution after high priority
- // └── Mixed priority execution scenarios
- //
- // 2. MULTI-LAYER PRIORITY HIERARCHY
- // ├── Three-Layer Priority System
- // │ ├── Base priority (lowest)
- // │ ├── Medium priority (middle)
- // │ ├── High priority (highest)
- // │ └── Execution order verification
- // │
- // ├── Complex Priority Chain Execution
- // │ ├── Nested scheduler execute() calls
- // │ ├── Priority evaluation sequence
- // │ ├── Scheduling overhead measurement
- // │ └── Performance impact validation
- // │
- // └── Priority Collision Handling
- // ├── Same-time scheduling of different priorities
- // ├── Priority-based task selection
- // ├── Execution sequence preservation
- // └── Timing accuracy in priority scenarios
- //
- // 3. ADVANCED PRIORITY SCENARIOS
- // ├── Dynamic Priority Changes
- // │ ├── Runtime scheduler hierarchy modification
- // │ ├── Task migration between priority levels
- // │ ├── Priority scheduler enable/disable
- // │ └── Scheduler state management
- // │
- // ├── Priority with Task Features
- // │ ├── Priority + scheduling options integration
- // │ ├── Priority + status request coordination
- // │ ├── Priority + timeout handling
- // │ └── Priority + self-destruct behavior
- // │
- // └── Real-World Priority Use Cases
- // ├── Time-critical sensor reading (high priority)
- // ├── Background data processing (base priority)
- // ├── Emergency response handling (highest priority)
- // └── Resource-intensive calculations (low priority)
- //
- // 4. PRIORITY PERFORMANCE AND OVERHEAD
- // ├── Scheduling Overhead Analysis
- // │ ├── Single scheduler vs. layered performance
- // │ ├── Execution time measurement
- // │ ├── Priority evaluation cost
- // │ └── Overhead scaling with priority layers
- // │
- // ├── Timing Accuracy with Priorities
- // │ ├── High priority task timing precision
- // │ ├── Base priority task timing impact
- // │ ├── Priority-induced scheduling delays
- // │ └── Overall system timing accuracy
- // │
- // └── Resource Utilization
- // ├── Memory usage with priority layers
- // ├── CPU utilization patterns
- // ├── Stack depth analysis
- // └── Performance optimization guidelines
- //
- // =====================================================================================
- // Enable priority functionality for comprehensive testing
- #define _TASK_PRIORITY // Layered task prioritization
- #define _TASK_WDT_IDS
- #define _TASK_TIMECRITICAL
- #include <gtest/gtest.h>
- #include "Arduino.h"
- #include "TaskScheduler.h"
- // Global test state for priority testing
- std::vector<std::string> priority_test_output;
- int priority_callback_counter = 0;
- unsigned long priority_execution_times[20]; // Store execution timestamps
- int priority_execution_index = 0;
- // Definition for test_output vector used by Arduino.h mock
- std::vector<std::string> test_output;
- // Test callback functions (no lambda functions)
- /**
- * @brief Base priority task callback - simulates normal priority work
- */
- void base_priority_callback() {
- priority_callback_counter++;
- priority_test_output.push_back("base_priority_executed");
- priority_execution_times[priority_execution_index++] = millis();
- std::cout << "Base priority task executed at " << millis() << "ms" << std::endl;
- }
- /**
- * @brief High priority task callback - simulates critical priority work
- */
- void high_priority_callback() {
- priority_callback_counter++;
- priority_test_output.push_back("high_priority_executed");
- priority_execution_times[priority_execution_index++] = millis();
- std::cout << "High priority task executed at " << millis() << "ms" << std::endl;
- }
- /**
- * @brief Priority test callback that uses currentScheduler() and currentTask()
- *
- * This callback mimics the example11 pattern by accessing the current scheduler
- * and task to get task ID and timing information.
- */
- void priority_test_callback() {
- priority_callback_counter++;
- // Use currentScheduler() and currentTask() like in example11
- Scheduler& current_scheduler = Scheduler::currentScheduler();
- Task& current_task = current_scheduler.currentTask();
- // Record task execution with ID for verification
- unsigned int task_id = current_task.getId();
- unsigned long execution_time = millis();
- priority_execution_times[priority_execution_index++] = execution_time;
- // Create output string with task ID for verification
- std::string output = "task_" + std::to_string(task_id) + "_executed";
- priority_test_output.push_back(output);
- std::cout << "Task: " << task_id << " executed at " << execution_time
- << "ms, Start delay = " << current_task.getStartDelay() << std::endl;
- }
- /**
- * @brief Medium priority task callback - simulates intermediate priority work
- */
- void medium_priority_callback() {
- priority_callback_counter++;
- priority_test_output.push_back("medium_priority_executed");
- priority_execution_times[priority_execution_index++] = millis();
- std::cout << "Medium priority task executed at " << millis() << "ms" << std::endl;
- }
- /**
- * @brief Time-critical sensor callback - simulates gyroscope/accelerometer reading
- */
- void sensor_critical_callback() {
- priority_callback_counter++;
- priority_test_output.push_back("sensor_critical_executed");
- priority_execution_times[priority_execution_index++] = millis();
- // Simulate precise sensor reading requiring minimal delay
- }
- /**
- * @brief Background processing callback - simulates non-critical background work
- */
- void background_processing_callback() {
- priority_callback_counter++;
- priority_test_output.push_back("background_processing_executed");
- priority_execution_times[priority_execution_index++] = millis();
- // Simulate background data processing
- }
- /**
- * @brief Emergency response callback - simulates highest priority emergency handling
- */
- void emergency_response_callback() {
- priority_callback_counter++;
- priority_test_output.push_back("emergency_response_executed");
- priority_execution_times[priority_execution_index++] = millis();
- // Simulate emergency response requiring immediate attention
- }
- /**
- * @brief Test fixture class for TaskScheduler priority functionality
- *
- * Provides setup and teardown for priority tests, ensuring clean state
- * between tests and utility methods for priority scenario validation.
- */
- class PrioritySchedulerTest : public ::testing::Test {
- protected:
- /**
- * @brief Set up test environment for priority testing
- *
- * Clears all test state and initializes timing system for priority tests.
- */
- void SetUp() override {
- clearPriorityTestOutput();
- priority_callback_counter = 0;
- priority_execution_index = 0;
- memset(priority_execution_times, 0, sizeof(priority_execution_times));
- millis(); // Initialize timing
- }
- /**
- * @brief Clean up test environment after priority tests
- *
- * Ensures no test artifacts affect subsequent tests.
- */
- void TearDown() override {
- clearPriorityTestOutput();
- priority_callback_counter = 0;
- priority_execution_index = 0;
- memset(priority_execution_times, 0, sizeof(priority_execution_times));
- }
- /**
- * @brief Helper to run scheduler until condition or timeout for priority tests
- */
- bool runPrioritySchedulerUntil(Scheduler& ts, std::function<bool()> condition, unsigned long timeout_ms = 2000) {
- return waitForCondition([&]() {
- ts.execute();
- return condition();
- }, timeout_ms);
- }
- /**
- * @brief Clear priority test output buffer
- */
- void clearPriorityTestOutput() {
- priority_test_output.clear();
- }
- /**
- * @brief Get count of priority test output entries
- */
- size_t getPriorityTestOutputCount() {
- return priority_test_output.size();
- }
- /**
- * @brief Get specific priority test output entry
- */
- std::string getPriorityTestOutput(size_t index) {
- if (index < priority_test_output.size()) {
- return priority_test_output[index];
- }
- return "";
- }
- /**
- * @brief Verify execution order matches expected priority sequence
- */
- bool verifyExecutionOrder(const std::vector<std::string>& expected_order) {
- if (expected_order.size() != priority_test_output.size()) {
- return false;
- }
- for (size_t i = 0; i < expected_order.size(); i++) {
- if (expected_order[i] != priority_test_output[i]) {
- return false;
- }
- }
- return true;
- }
- /**
- * @brief Analyze priority evaluation pattern based on example11 sequence
- *
- * According to wiki: "entire chain of tasks of the higher priority scheduler
- * is executed for every single step (task) of the lower priority chain"
- *
- * Expected pattern: high priority tasks are evaluated between each base task
- */
- bool validatePriorityEvaluationPattern() {
- // Look for the pattern where high priority tasks (4,5) are frequently interspersed
- // with base priority tasks (1,2,3), especially task 4 which has shortest interval
- int consecutive_base_tasks = 0;
- int max_consecutive_base = 0;
- for (size_t i = 0; i < getPriorityTestOutputCount(); i++) {
- std::string task = getPriorityTestOutput(i);
- if (task == "task_1_executed" || task == "task_2_executed" || task == "task_3_executed") {
- consecutive_base_tasks++;
- max_consecutive_base = std::max(max_consecutive_base, consecutive_base_tasks);
- } else {
- consecutive_base_tasks = 0;
- }
- }
- // In proper priority scheduling, we shouldn't see many consecutive base tasks
- // because high priority tasks should be evaluated frequently
- return max_consecutive_base <= 3; // Allow some consecutive base tasks but not too many
- }
- };
- // ================== BASIC PRIORITY FUNCTIONALITY TESTS ==================
- /**
- * @brief Test basic scheduler hierarchy setup and validation (based on example11)
- *
- * TESTS: setHighPriorityScheduler(), currentScheduler(), currentTask()
- *
- * PURPOSE: Verify that scheduler hierarchy can be established correctly
- * and that the priority relationships work as expected for basic scenarios,
- * following the pattern from Scheduler_example11_Priority.
- *
- * PRIORITY HIERARCHY SETUP (from example11):
- * - Base scheduler: Tasks t1, t2, t3 (1000ms, 2000ms, 3000ms intervals)
- * - High scheduler: Tasks t4, t5 (500ms, 1000ms intervals)
- * - Hierarchy relationship: base.setHighPriorityScheduler(&high)
- * - Execution order follows priority evaluation sequence: 4,5,1,4,5,2,4,5,3
- *
- * TEST SCENARIO:
- * 1. Create base and high priority schedulers matching example11
- * 2. Establish hierarchy relationship
- * 3. Add tasks to both schedulers with example11 intervals
- * 4. Execute base scheduler and verify priority execution pattern
- * 5. Verify currentScheduler() and currentTask() work correctly
- *
- * EXPECTATIONS:
- * - High priority tasks (t4, t5) execute more frequently
- * - currentScheduler() returns correct scheduler during execution
- * - Task IDs can be retrieved and verified
- * - Priority evaluation follows documented sequence
- */
- TEST_F(PrioritySchedulerTest, BasicSchedulerHierarchy) {
- // Create base and high priority schedulers (matching example11)
- Scheduler base_scheduler;
- Scheduler high_scheduler;
- // Establish hierarchy - high scheduler has priority over base
- base_scheduler.setHighPriorityScheduler(&high_scheduler);
- // Add tasks to schedulers with intervals matching example11
- // Base priority tasks: longer intervals
- Task t1(1000, 3, &priority_test_callback, &base_scheduler, false); // 1000ms interval
- Task t2(2000, 2, &priority_test_callback, &base_scheduler, false); // 2000ms interval
- Task t3(3000, 1, &priority_test_callback, &base_scheduler, false); // 3000ms interval
- // High priority tasks: shorter intervals for more frequent execution
- Task t4(500, 6, &priority_test_callback, &high_scheduler, false); // 500ms interval
- Task t5(1000, 3, &priority_test_callback, &high_scheduler, false); // 1000ms interval
- // Set task IDs for identification (like in example11)
- t1.setId(1);
- t2.setId(2);
- t3.setId(3);
- t4.setId(4);
- t5.setId(5);
- // Enable all tasks recursively (like example11: enableAll(true))
- base_scheduler.enableAll(true);
- // Verify tasks are enabled
- EXPECT_TRUE(t1.isEnabled());
- EXPECT_TRUE(t2.isEnabled());
- EXPECT_TRUE(t3.isEnabled());
- EXPECT_TRUE(t4.isEnabled());
- EXPECT_TRUE(t5.isEnabled());
- // Execute scheduler for sufficient time to see priority pattern
- // High priority tasks should execute more frequently due to shorter intervals
- bool success = runPrioritySchedulerUntil(base_scheduler, []() {
- return priority_callback_counter >= 15; // 6+3=9 high + 3+2+1=6 base = 15 total
- }, 5000);
- EXPECT_TRUE(success);
- EXPECT_EQ(priority_callback_counter, 15);
- // Count executions by task ID to verify the priority pattern
- int task1_count = 0, task2_count = 0, task3_count = 0; // Base priority tasks
- int task4_count = 0, task5_count = 0; // High priority tasks
- for (size_t i = 0; i < getPriorityTestOutputCount(); i++) {
- std::string output = getPriorityTestOutput(i);
- if (output == "task_1_executed") task1_count++;
- else if (output == "task_2_executed") task2_count++;
- else if (output == "task_3_executed") task3_count++;
- else if (output == "task_4_executed") task4_count++;
- else if (output == "task_5_executed") task5_count++;
- }
- // Verify execution counts match expected iterations
- EXPECT_EQ(task1_count, 3); // t1: 3 executions (1000ms interval)
- EXPECT_EQ(task2_count, 2); // t2: 2 executions (2000ms interval)
- EXPECT_EQ(task3_count, 1); // t3: 1 execution (3000ms interval)
- EXPECT_EQ(task4_count, 6); // t4: 6 executions (500ms interval, high priority)
- EXPECT_EQ(task5_count, 3); // t5: 3 executions (1000ms interval, high priority)
- // Verify that task 4 (highest frequency, high priority) executed first
- // Task 4 has 500ms interval and high priority, so it should execute first
- EXPECT_EQ(getPriorityTestOutput(0), "task_4_executed");
- // Count high vs base priority executions for overall pattern verification
- int high_priority_total = task4_count + task5_count; // 6 + 3 = 9
- int base_priority_total = task1_count + task2_count + task3_count; // 3 + 2 + 1 = 6
- EXPECT_EQ(high_priority_total, 9);
- EXPECT_EQ(base_priority_total, 6);
- // Validate the priority execution order pattern from example11 output
- // Expected initial sequence: 4, 5, 1, 2, 3 (based on timing and priority)
- // Task 4 (ID 40 in example) executes at 0ms, 500ms, 1000ms, 1500ms, 2000ms, 2500ms
- // Task 5 (ID 50 in example) executes at 10ms, 1010ms, 2011ms, 3010ms, etc.
- // Task 1 executes at ~21ms, 1021ms, 2022ms, 3021ms, etc.
- // Verify high priority tasks execute more frequently in early execution
- std::vector<std::string> early_executions;
- for (size_t i = 0; i < std::min((size_t)10, getPriorityTestOutputCount()); i++) {
- early_executions.push_back(getPriorityTestOutput(i));
- }
- // Count high priority vs base priority in first 10 executions
- int early_high_count = 0;
- int early_base_count = 0;
- for (const auto& exec : early_executions) {
- if (exec == "task_4_executed" || exec == "task_5_executed") {
- early_high_count++;
- } else if (exec == "task_1_executed" || exec == "task_2_executed" || exec == "task_3_executed") {
- early_base_count++;
- }
- }
- // High priority tasks should dominate early execution due to shorter intervals
- EXPECT_GE(early_high_count, early_base_count);
- // Verify task 4 appears multiple times in early execution (due to 500ms interval)
- int task4_early_count = 0;
- for (const auto& exec : early_executions) {
- if (exec == "task_4_executed") {
- task4_early_count++;
- }
- }
- EXPECT_GE(task4_early_count, 2); // Task 4 should execute at least twice in early sequence
- // Validate that base priority tasks eventually execute
- // Task 1 should appear at least once given its 1000ms interval
- bool task1_found = false;
- bool task2_found = (task2_count > 0); // Task 2 might not execute in early sequence due to 2000ms interval
- for (const auto& exec : priority_test_output) {
- if (exec == "task_1_executed") {
- task1_found = true;
- break;
- }
- }
- EXPECT_TRUE(task1_found);
- // Print execution sequence for debugging (similar to example11 output)
- std::cout << "\nPriority Test Execution Sequence:" << std::endl;
- for (size_t i = 0; i < getPriorityTestOutputCount() && i < 15; i++) {
- std::string task_id = getPriorityTestOutput(i);
- // Extract task number from "task_X_executed"
- if (task_id.length() > 5) {
- char task_num = task_id[5];
- std::cout << "Task: " << task_num << " at position " << i << std::endl;
- }
- }
- // Validate the specific execution order pattern from example11 output
- // This ensures the priority evaluation sequence matches documented behavior
- EXPECT_TRUE(validatePriorityEvaluationPattern())
- << "Priority evaluation pattern validation failed - high priority tasks should be interspersed with base tasks";
- }
- /**
- * @brief Test two-layer priority scheduling with timing validation
- *
- * TESTS: Priority-based execution timing and sequence
- *
- * PURPOSE: Verify that two-layer priority scheduling works correctly
- * with proper timing relationships and execution sequence preservation.
- *
- * PRIORITY EXECUTION PATTERN:
- * According to wiki documentation:
- * "Task prioritization is achieved by executing the entire chain of tasks
- * of the higher priority scheduler for every single step (task) of the
- * lower priority chain."
- *
- * For 5 base tasks and 2 high priority tasks:
- * Execution sequence: H1 -> H2 -> B1 -> H1 -> H2 -> B2 -> H1 -> H2 -> B3 -> H1 -> H2 -> B4 -> H1 -> H2 -> B5
- *
- * TEST SCENARIO:
- * 1. Set up base scheduler with 5 tasks at 100ms intervals
- * 2. Set up high scheduler with 2 tasks at 50ms intervals
- * 3. Run for sufficient time to see priority pattern
- * 4. Verify high priority tasks get evaluated more frequently
- * 5. Validate timing relationships
- */
- TEST_F(PrioritySchedulerTest, TwoLayerPriorityTiming) {
- Scheduler base_scheduler;
- Scheduler high_scheduler;
- base_scheduler.setHighPriorityScheduler(&high_scheduler);
- // Base priority tasks - longer intervals
- Task base_task1(200, 3, &base_priority_callback, &base_scheduler, true);
- // High priority tasks - shorter intervals for more frequent execution
- Task high_task1(50, 6, &high_priority_callback, &high_scheduler, true);
- // Run scheduler and measure execution patterns
- unsigned long start_time = millis();
- bool success = runPrioritySchedulerUntil(base_scheduler, []() {
- return priority_callback_counter >= 9; // 6 high + 3 base = 9 total
- }, 3000);
- EXPECT_TRUE(success);
- EXPECT_EQ(priority_callback_counter, 9);
- // Verify that high priority tasks executed more frequently
- int high_priority_count = 0;
- int base_priority_count = 0;
- for (size_t i = 0; i < getPriorityTestOutputCount(); i++) {
- if (getPriorityTestOutput(i) == "high_priority_executed") {
- high_priority_count++;
- } else if (getPriorityTestOutput(i) == "base_priority_executed") {
- base_priority_count++;
- }
- }
- EXPECT_EQ(high_priority_count, 6);
- EXPECT_EQ(base_priority_count, 3);
- // High priority tasks should execute first due to shorter interval
- EXPECT_EQ(getPriorityTestOutput(0), "high_priority_executed");
- }
- /**
- * @brief Test priority collision handling - tasks scheduled at same time
- *
- * TESTS: Priority-based task selection when multiple tasks are ready
- *
- * PURPOSE: Verify that when multiple tasks from different priority levels
- * are ready to execute simultaneously, the priority system correctly
- * selects high priority tasks first.
- */
- TEST_F(PrioritySchedulerTest, PriorityCollisionHandling) {
- Scheduler base_scheduler;
- Scheduler high_scheduler;
- base_scheduler.setHighPriorityScheduler(&high_scheduler);
- // Set tasks to execute at similar times to create scheduling collision
- Task base_task(100, 1, &base_priority_callback, &base_scheduler, false);
- Task high_task(100, 1, &high_priority_callback, &high_scheduler, false);
- // Enable both tasks at the same time
- base_task.enable();
- high_task.enable();
- // Execute scheduler - both tasks are ready immediately
- bool success = runPrioritySchedulerUntil(base_scheduler, []() {
- return priority_callback_counter >= 2;
- });
- EXPECT_TRUE(success);
- EXPECT_EQ(priority_callback_counter, 2);
- // High priority task should execute first in collision scenario
- EXPECT_EQ(getPriorityTestOutput(0), "high_priority_executed");
- EXPECT_EQ(getPriorityTestOutput(1), "base_priority_executed");
- }
- // ================== MULTI-LAYER PRIORITY HIERARCHY TESTS ==================
- /**
- * @brief Test three-layer priority hierarchy
- *
- * TESTS: Complex multi-layer priority scheduling
- *
- * PURPOSE: Verify that three-layer priority hierarchy works correctly
- * with proper execution order: Emergency -> High -> Base priority.
- */
- TEST_F(PrioritySchedulerTest, ThreeLayerPriorityHierarchy) {
- Scheduler base_scheduler;
- Scheduler high_scheduler;
- Scheduler emergency_scheduler;
- // Establish three-layer hierarchy
- // Emergency (highest) -> High -> Base (lowest)
- high_scheduler.setHighPriorityScheduler(&emergency_scheduler);
- base_scheduler.setHighPriorityScheduler(&high_scheduler);
- // Add tasks to each priority level
- Task base_task(100, 2, &base_priority_callback, &base_scheduler, true);
- Task high_task(100, 2, &high_priority_callback, &high_scheduler, true);
- Task emergency_task(100, 1, &emergency_response_callback, &emergency_scheduler, true);
- // Execute base scheduler - should process all layers in priority order
- bool success = runPrioritySchedulerUntil(base_scheduler, []() {
- return priority_callback_counter >= 5; // 1 emergency + 2 high + 2 base = 5
- });
- EXPECT_TRUE(success);
- EXPECT_EQ(priority_callback_counter, 5);
- // Verify execution order follows three-layer priority
- EXPECT_EQ(getPriorityTestOutput(0), "emergency_response_executed");
- // High priority tasks should execute next
- bool found_high_after_emergency = false;
- for (size_t i = 1; i < getPriorityTestOutputCount(); i++) {
- if (getPriorityTestOutput(i) == "high_priority_executed") {
- found_high_after_emergency = true;
- break;
- }
- }
- EXPECT_TRUE(found_high_after_emergency);
- }
- /**
- * @brief Test priority scheduling overhead measurement
- *
- * TESTS: Performance impact of layered priority scheduling
- *
- * PURPOSE: Validate that priority scheduling overhead is reasonable
- * and measure the performance impact of different priority configurations.
- *
- * According to wiki: "Scheduling overhead of a 3 layer prioritization
- * approach is 3 times higher than that of a flat execution chain."
- */
- TEST_F(PrioritySchedulerTest, PrioritySchedulingOverhead) {
- // Test 1: Single scheduler (baseline)
- {
- clearPriorityTestOutput();
- priority_callback_counter = 0;
- Scheduler single_scheduler;
- Task task1(50, 10, &base_priority_callback, &single_scheduler, true);
- unsigned long start_time = millis();
- runPrioritySchedulerUntil(single_scheduler, []() {
- return priority_callback_counter >= 10;
- });
- unsigned long single_scheduler_time = millis() - start_time;
- EXPECT_EQ(priority_callback_counter, 10);
- std::cout << "Single scheduler time: " << single_scheduler_time << "ms" << std::endl;
- }
- // Test 2: Two-layer priority scheduler
- {
- clearPriorityTestOutput();
- priority_callback_counter = 0;
- Scheduler base_scheduler;
- Scheduler high_scheduler;
- base_scheduler.setHighPriorityScheduler(&high_scheduler);
- Task base_task(50, 5, &base_priority_callback, &base_scheduler, true);
- Task high_task(50, 5, &high_priority_callback, &high_scheduler, true);
- unsigned long start_time = millis();
- runPrioritySchedulerUntil(base_scheduler, []() {
- return priority_callback_counter >= 10;
- });
- unsigned long priority_scheduler_time = millis() - start_time;
- EXPECT_EQ(priority_callback_counter, 10);
- std::cout << "Priority scheduler time: " << priority_scheduler_time << "ms" << std::endl;
- // Priority scheduling should be slower but still reasonable
- // We don't enforce strict timing due to test environment variability
- EXPECT_GT(priority_scheduler_time, 0);
- }
- }
- // ================== ADVANCED PRIORITY SCENARIOS ==================
- /**
- * @brief Test real-world priority use case - sensor + background processing
- *
- * TESTS: Realistic priority scenario with time-critical and background tasks
- *
- * PURPOSE: Validate priority system works for real-world scenarios like
- * time-critical sensor reading with background data processing.
- *
- * SCENARIO:
- * - High priority: Time-critical sensor reading (gyroscope/accelerometer)
- * - Base priority: Background data processing and housekeeping
- * - Emergency priority: Safety monitoring and emergency response
- */
- TEST_F(PrioritySchedulerTest, RealWorldSensorPriorityScenario) {
- Scheduler base_scheduler;
- Scheduler sensor_scheduler;
- Scheduler emergency_scheduler;
- // Set up three-layer priority: Emergency > Sensor > Background
- sensor_scheduler.setHighPriorityScheduler(&emergency_scheduler);
- base_scheduler.setHighPriorityScheduler(&sensor_scheduler);
- // Background processing - base priority, longer intervals
- Task background_task(500, 2, &background_processing_callback, &base_scheduler, true);
- // Time-critical sensor reading - high priority, short intervals
- Task sensor_task(10, 10, &sensor_critical_callback, &sensor_scheduler, true);
- // Emergency monitoring - highest priority, triggered as needed
- Task emergency_task(1000, 1, &emergency_response_callback, &emergency_scheduler, true);
- // Run scenario and verify sensor tasks get priority
- bool success = runPrioritySchedulerUntil(base_scheduler, []() {
- return priority_callback_counter >= 13; // 1 emergency + 10 sensor + 2 background
- }, 3000);
- EXPECT_TRUE(success);
- EXPECT_EQ(priority_callback_counter, 13);
- // Count executions by priority level
- int emergency_count = 0;
- int sensor_count = 0;
- int background_count = 0;
- for (size_t i = 0; i < getPriorityTestOutputCount(); i++) {
- std::string output = getPriorityTestOutput(i);
- if (output == "emergency_response_executed") {
- emergency_count++;
- } else if (output == "sensor_critical_executed") {
- sensor_count++;
- } else if (output == "background_processing_executed") {
- background_count++;
- }
- }
- EXPECT_EQ(emergency_count, 1);
- EXPECT_EQ(sensor_count, 10);
- EXPECT_EQ(background_count, 2);
- // Emergency should execute first if present
- EXPECT_EQ(getPriorityTestOutput(0), "emergency_response_executed");
- }
- /**
- * @brief Test dynamic priority scheduler changes
- *
- * TESTS: Runtime modification of scheduler hierarchy
- *
- * PURPOSE: Verify that priority relationships can be changed at runtime
- * and that the system adapts correctly to new priority configurations.
- */
- TEST_F(PrioritySchedulerTest, DynamicPriorityChanges) {
- Scheduler base_scheduler;
- Scheduler high_scheduler;
- Scheduler alternative_scheduler;
- // Initial setup: base -> high priority
- base_scheduler.setHighPriorityScheduler(&high_scheduler);
- Task base_task(100, 1, &base_priority_callback, &base_scheduler, true);
- Task high_task(100, 1, &high_priority_callback, &high_scheduler, true);
- Task alt_task(100, 1, &medium_priority_callback, &alternative_scheduler, true);
- // Execute with initial hierarchy
- bool success = runPrioritySchedulerUntil(base_scheduler, []() {
- return priority_callback_counter >= 2;
- });
- EXPECT_TRUE(success);
- EXPECT_EQ(priority_callback_counter, 2);
- EXPECT_EQ(getPriorityTestOutput(0), "high_priority_executed");
- EXPECT_EQ(getPriorityTestOutput(1), "base_priority_executed");
- // Change priority hierarchy at runtime
- clearPriorityTestOutput();
- priority_callback_counter = 0;
- base_scheduler.setHighPriorityScheduler(&alternative_scheduler);
- // Re-enable tasks for another round
- base_task.restart();
- alt_task.restart();
- // Execute with new hierarchy
- success = runPrioritySchedulerUntil(base_scheduler, []() {
- return priority_callback_counter >= 2;
- });
- EXPECT_TRUE(success);
- EXPECT_EQ(priority_callback_counter, 2);
- EXPECT_EQ(getPriorityTestOutput(0), "medium_priority_executed");
- EXPECT_EQ(getPriorityTestOutput(1), "base_priority_executed");
- }
- /**
- * @brief Test priority system with enableAll/disableAll recursive operations
- *
- * TESTS: Recursive enable/disable operations across priority layers
- *
- * PURPOSE: Verify that enableAll(true) and disableAll(true) work correctly
- * with priority schedulers, affecting all layers in the hierarchy.
- */
- TEST_F(PrioritySchedulerTest, PriorityRecursiveEnableDisable) {
- Scheduler base_scheduler;
- Scheduler high_scheduler;
- base_scheduler.setHighPriorityScheduler(&high_scheduler);
- // Add tasks to both schedulers (initially disabled)
- Task base_task(100, 2, &base_priority_callback, &base_scheduler, false);
- Task high_task(100, 2, &high_priority_callback, &high_scheduler, false);
- // Verify tasks are initially disabled
- EXPECT_FALSE(base_task.isEnabled());
- EXPECT_FALSE(high_task.isEnabled());
- // Enable all tasks recursively (should enable high priority tasks too)
- base_scheduler.enableAll(true);
- EXPECT_TRUE(base_task.isEnabled());
- EXPECT_TRUE(high_task.isEnabled());
- // Execute to verify both schedulers work
- bool success = runPrioritySchedulerUntil(base_scheduler, []() {
- return priority_callback_counter >= 4;
- });
- EXPECT_TRUE(success);
- EXPECT_EQ(priority_callback_counter, 4);
- // Disable all tasks recursively
- base_scheduler.disableAll(true);
- EXPECT_FALSE(base_task.isEnabled());
- EXPECT_FALSE(high_task.isEnabled());
- }
- /**
- * @brief Main test runner function for priority functionality
- *
- * Initializes Google Test framework and runs all priority feature tests.
- * Called by the test execution environment to start testing process.
- */
- int main(int argc, char **argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
- }
|