diff --git a/src/tests/scheduler/test-taskscheduler.cpp b/src/tests/scheduler/test-taskscheduler.cpp index 1ac40adbd241aa0733b73f77cb30ab4c50d36e2f..bf842aa1f0db2459bde6148dd5dc667e1950f45e 100644 --- a/src/tests/scheduler/test-taskscheduler.cpp +++ b/src/tests/scheduler/test-taskscheduler.cpp @@ -22,6 +22,8 @@ #include <scheduler/TaskScheduler.h> +namespace +{ using namespace Boardcore; using namespace miosix; @@ -31,13 +33,18 @@ GpioPin pin3 = GpioPin(GPIOB_BASE, 15); GpioPin pin4 = GpioPin(GPIOD_BASE, 8); GpioPin pin5 = GpioPin(GPIOD_BASE, 9); +bool taskLogEnabled; ///< A flag to enable/disable task logging + void task2Hz() { pin1.high(); delayUs(1); pin1.low(); - printf("2 Hz tick\n"); + if (taskLogEnabled) + { + printf("2 Hz tick\n"); + } } void task5Hz() @@ -45,6 +52,11 @@ void task5Hz() pin2.high(); delayUs(1); pin2.low(); + + if (taskLogEnabled) + { + printf("5 Hz tick\n"); + } } void task500Hz() @@ -70,7 +82,17 @@ void signalPin5() void blockingTask() { delayMs(100); } -int main() +// Shared task functions between tests +TaskScheduler::function_t f2Hz{&task2Hz}; ///< A 2 Hz task +TaskScheduler::function_t f5Hz{&task5Hz}; ///< A 5 Hz task +TaskScheduler::function_t f500Hz{&task500Hz}; ///< A 500 Hz task +TaskScheduler::function_t f1KHz{&task1KHz}; ///< A 1 KHz task +TaskScheduler::function_t blockingF{&blockingTask}; ///< A blocking task + +/** + * @brief Sets up the output pins + */ +void setup() { pin1.mode(Mode::OUTPUT); pin1.low(); @@ -83,17 +105,36 @@ int main() pin5.mode(Mode::OUTPUT); pin5.low(); - TaskScheduler scheduler; + taskLogEnabled = true; +} - TaskScheduler::function_t f2Hz{&task2Hz}; - TaskScheduler::function_t f5Hz{&task5Hz}; - TaskScheduler::function_t f500Hz{&task500Hz}; - TaskScheduler::function_t f1KHz{&task1KHz}; - TaskScheduler::function_t blockingF{&blockingTask}; +void printTaskStats(TaskScheduler& scheduler) +{ + printf("Tasks stats:\n"); + for (auto stat : scheduler.getTaskStats()) + { + printf("- %d:\n", stat.id); + printf("\tActivation: %.2f, %.2f\n", stat.activationStats.mean, + stat.activationStats.stdDev); + printf("\tPeriod: %.2f, %.2f\n", stat.periodStats.mean, + stat.periodStats.stdDev); + printf("\tWorkload: %.2f, %.2f\n", stat.workloadStats.mean, + stat.workloadStats.stdDev); + printf("\tMissed events: %ld\n", stat.missedEvents); + printf("\tFailed events: %ld\n", stat.failedEvents); + } +} - scheduler.addTask(f2Hz, 500); +/** + * @brief Tests the most common usage patters of the scheduler + */ +void test_general_purpose() +{ + TaskScheduler scheduler{}; + + int task1 = scheduler.addTask(f2Hz, 500); scheduler.addTask(f5Hz, 200); - scheduler.addTask(f500Hz, 2, TaskScheduler::Policy::RECOVER); + int task3 = scheduler.addTask(f500Hz, 2, TaskScheduler::Policy::RECOVER); scheduler.addTask(f1KHz, 1, TaskScheduler::Policy::RECOVER); scheduler.addTask(f1KHz, 1, TaskScheduler::Policy::RECOVER); @@ -106,26 +147,28 @@ int main() if (!scheduler.start()) { printf("Error starting the task scheduler\n"); - return 0; + return; } Thread::sleep(4 * 1000); signalPin5(); + scheduler.disableTask(task1); + scheduler.disableTask(task3); printf("Removed tasks 1 (2Hz) and 3 (500Hz)\n"); Thread::sleep(4 * 1000); printf("Now re-adding task 1 and 3\n"); signalPin5(); - scheduler.addTask(f2Hz, 500); - scheduler.addTask(f500Hz, 2, TaskScheduler::Policy::RECOVER); + scheduler.enableTask(task1); + scheduler.enableTask(task3); Thread::sleep(4 * 1000); printf("Now adding a one shot task which will take 100ms to complete\n"); signalPin5(); - // scheduler.addTask(blockingF, 0, TaskScheduler::Policy::ONE_SHOT); + scheduler.addTask(blockingF, 0, TaskScheduler::Policy::ONE_SHOT); Thread::sleep(4 * 1000); @@ -133,17 +176,237 @@ int main() scheduler.stop(); printf("Task scheduler stopped\n"); - printf("Tasks stats:\n"); - for (auto stat : scheduler.getTaskStats()) + printTaskStats(scheduler); +} + +/** + * @brief Tests scheduler with a full task list + */ +void test_fill_scheduler() +{ + TaskScheduler scheduler{}; + + printf("Adding tasks until the scheduler is full\n"); + size_t taskCount = 0; + // Fill up the scheduler with tasks + do { - printf("- %d:\n", stat.id); - printf("\tActivation: %.2f, %.2f\n", stat.activationStats.mean, - stat.activationStats.stdDev); - printf("\tPeriod: %.2f, %.2f\n", stat.periodStats.mean, - stat.periodStats.stdDev); - printf("\tWorkload: %.2f, %.2f\n", stat.workloadStats.mean, - stat.workloadStats.stdDev); - printf("\tMissed events: %lu\n", stat.missedEvents); - printf("\tFailed events: %lu\n", stat.failedEvents); + size_t lastId = + scheduler.addTask(f2Hz, 500, TaskScheduler::Policy::ONE_SHOT); + if (!lastId) + { + break; + } + taskCount++; + } while (true); + + // Subtract one because the 0-th task is reserved + if (taskCount != TaskScheduler::MAX_TASKS - 1) + { + printf("Error: couldn't fill the scheduler: taskCount = %zu \n", + taskCount); + return; + } + + printf("Done adding tasks: taskCount = %zu\n", taskCount); + + printf("Trying to add another task\n"); + // Try to add another task + if (scheduler.addTask(f2Hz, 500, TaskScheduler::Policy::ONE_SHOT)) + { + printf("Error: added a task when the scheduler was full\n"); + return; + } + + printf("Added tasks successfully\n"); + + printf("Starting the scheduler\n"); + scheduler.start(); + + Thread::sleep(4 * 1000); + + printf("Stopping the scheduler\n"); + scheduler.stop(); + + printTaskStats(scheduler); +} + +/** + * @brief Tests the addTask function while the scheduler is running + */ +void test_runtime_add_task() +{ + TaskScheduler scheduler{}; + + scheduler.addTask(f2Hz, 500); + scheduler.addTask(f500Hz, 2, TaskScheduler::Policy::RECOVER); + + printf("2 tasks added (2Hz 500Hz)\n"); + printf("Starting the scheduler\n"); + scheduler.start(); + + Thread::sleep(4 * 1000); + + printf("Adding a new task (5Hz)\n"); + if (!scheduler.addTask(f5Hz, 200)) + { + printf("Error adding task while the scheduler is running\n"); } + + Thread::sleep(4 * 1000); + + printf("Stopping the scheduler\n"); + scheduler.stop(); +} + +/** + * @brief Tests the enable/disable functions of the scheduler + */ +void test_enable_disable() +{ + TaskScheduler scheduler{}; + + int task1 = scheduler.addTask(f2Hz, 500); + scheduler.addTask(f5Hz, 200); + int task3 = scheduler.addTask(f500Hz, 2, TaskScheduler::Policy::RECOVER); + + printf("3 tasks added (2Hz 5Hz 500Hz)\n"); + printf("Starting the scheduler\n"); + scheduler.start(); + + Thread::sleep(4 * 1000); + + printf("Disabling task 1 (2Hz) and 3 (500Hz)\n"); + scheduler.disableTask(task1); + scheduler.disableTask(task3); + + Thread::sleep(4 * 1000); + + printf("Re-enabling task 1 (2Hz) and 3 (500Hz)\n"); + scheduler.enableTask(task1); + scheduler.enableTask(task3); + + printf("Disabling task 2 (5Hz)\n"); + + Thread::sleep(2 * 1000); + + scheduler.stop(); + printf("Task scheduler stopped\n"); + + printTaskStats(scheduler); +} + +/** + * @brief Tests various edge cases of the scheduler: + * - Disabling out-of-range tasks + * - Enabling out-of-range tasks + * - Double start + */ +void test_edge_cases() +{ + TaskScheduler scheduler{}; + + scheduler.addTask(f2Hz, 500); + scheduler.addTask(f5Hz, 200); + scheduler.addTask(f500Hz, 2, TaskScheduler::Policy::RECOVER); + + printf("3 tasks added (2Hz 5Hz 500Hz)\n"); + printf("Starting the scheduler\n"); + scheduler.start(); + + printf("Starting the scheduler again"); + if (scheduler.start()) + { + printf("Error: started the scheduler twice\n"); + } + + Thread::sleep(1000); + + printf("Disabling out-of-range tasks with IDs 0 and 256"); + scheduler.disableTask(0); + scheduler.disableTask(256); + + Thread::sleep(1000); + + printf("Enabling out-of-range tasks with IDs 0 and 256"); + scheduler.enableTask(0); + scheduler.enableTask(256); + + Thread::sleep(1000); + + printf("Stopping the scheduler\n"); + scheduler.stop(); +} + +/** + * @brief Tests the scheduler with a general usage pattern for a prolonged + * period of time + */ +void test_long_range() +{ + TaskScheduler scheduler{}; + + scheduler.addTask(f2Hz, 500); + scheduler.addTask(f5Hz, 200); + scheduler.addTask(f500Hz, 2, TaskScheduler::Policy::RECOVER); + + printf("3 tasks added (2Hz 5Hz 500Hz)\n"); + printf("A message will be printed every minute\n"); + printf("Starting the scheduler\n"); + scheduler.start(); + + for (int i = 0; i < 5; i++) + { + printf("Scheduler running for %d minute(s)\n", i); + Thread::sleep(60 * 1000); + } + + printf("Stopping the scheduler\n"); + scheduler.stop(); +} + +} // namespace + +int main() +{ + setup(); + + // Avoid clutter from tasks since this test will add a lot of tasks + taskLogEnabled = false; + printf("=> Running the fill scheduler test\n"); + test_fill_scheduler(); + taskLogEnabled = true; + + printf("\n"); + + printf("=> Running the runtime add task test\n"); + test_runtime_add_task(); + + printf("\n"); + + printf("=> Running the enable/disable test\n"); + test_enable_disable(); + + printf("\n"); + + printf("=> Running the edge cases tests\n"); + test_edge_cases(); + + printf("\n"); + + printf("=> Running the general purpose test\n"); + test_general_purpose(); + + printf("\n"); + + // Avoid clutter from tasks since this test will run for a while + taskLogEnabled = false; + printf("=> Running the long range test, this may take a while\n"); + test_long_range(); + taskLogEnabled = true; + + printf("\n"); + + printf("\n=> Task Scheduler tests completed\n"); + return 0; }