From a1fb5693cef2823c71dd624718da2cbb98564952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niccol=C3=B2=20Betto?= <niccolo.betto@skywarder.eu> Date: Wed, 28 Dec 2022 16:28:24 +0100 Subject: [PATCH] [TaskScheduler] Update task scheduler test Updates the task scheduler test entrypoint to account for modifications prior to this commit. The test was also expanded to perform more thorough testing of the scheduler by targeting edge cases and track unintended behaviour under heavier usage. --- src/tests/scheduler/test-taskscheduler.cpp | 313 +++++++++++++++++++-- 1 file changed, 288 insertions(+), 25 deletions(-) diff --git a/src/tests/scheduler/test-taskscheduler.cpp b/src/tests/scheduler/test-taskscheduler.cpp index 1ac40adbd..bf842aa1f 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; } -- GitLab