2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
22 #include "scheduler/scheduler.h"
25 #include "unittest_macros.h"
26 #include "gtest/gtest.h"
28 const int TEST_GYRO_SAMPLE_HZ
= 8000;
29 const int TEST_GYRO_SAMPLE_TIME
= 10;
30 const int TEST_FILTERING_TIME
= 40;
31 const int TEST_PID_LOOP_TIME
= 58;
32 const int TEST_UPDATE_ACCEL_TIME
= 32;
33 const int TEST_UPDATE_ATTITUDE_TIME
= 28;
34 const int TEST_HANDLE_SERIAL_TIME
= 30;
35 const int TEST_UPDATE_BATTERY_TIME
= 1;
36 const int TEST_UPDATE_RX_CHECK_TIME
= 34;
37 const int TEST_UPDATE_RX_MAIN_TIME
= 1;
38 const int TEST_IMU_UPDATE_TIME
= 5;
39 const int TEST_DISPATCH_TIME
= 1;
41 #define TASK_COUNT_UNITTEST (TASK_BATTERY_VOLTAGE + 1)
42 #define TASK_PERIOD_HZ(hz) (1000000 / (hz))
45 task_t
* unittest_scheduler_selectedTask
;
46 uint8_t unittest_scheduler_selectedTaskDynPrio
;
47 uint16_t unittest_scheduler_waitingTasks
;
48 timeDelta_t unittest_scheduler_taskRequiredTimeUs
;
49 bool taskGyroRan
= false;
50 bool taskFilterRan
= false;
51 bool taskPidRan
= false;
52 bool taskFilterReady
= false;
53 bool taskPidReady
= false;
55 // set up micros() to simulate time
56 uint32_t simulatedTime
= 0;
57 uint32_t micros(void) { return simulatedTime
; }
59 // set up tasks to take a simulated representative time to execute
60 bool gyroFilterReady(void) { return taskFilterReady
; }
61 bool pidLoopReady(void) { return taskPidReady
; }
62 void taskGyroSample(timeUs_t
) { simulatedTime
+= TEST_GYRO_SAMPLE_TIME
; taskGyroRan
= true; }
63 void taskFiltering(timeUs_t
) { simulatedTime
+= TEST_FILTERING_TIME
; taskFilterRan
= true; }
64 void taskMainPidLoop(timeUs_t
) { simulatedTime
+= TEST_PID_LOOP_TIME
; taskPidRan
= true; }
65 void taskUpdateAccelerometer(timeUs_t
) { simulatedTime
+= TEST_UPDATE_ACCEL_TIME
; }
66 void taskHandleSerial(timeUs_t
) { simulatedTime
+= TEST_HANDLE_SERIAL_TIME
; }
67 void taskUpdateBatteryVoltage(timeUs_t
) { simulatedTime
+= TEST_UPDATE_BATTERY_TIME
; }
68 bool rxUpdateCheck(timeUs_t
, timeDelta_t
) { simulatedTime
+= TEST_UPDATE_RX_CHECK_TIME
; return false; }
69 void taskUpdateRxMain(timeUs_t
) { simulatedTime
+= TEST_UPDATE_RX_MAIN_TIME
; }
70 void imuUpdateAttitude(timeUs_t
) { simulatedTime
+= TEST_IMU_UPDATE_TIME
; }
71 void dispatchProcess(timeUs_t
) { simulatedTime
+= TEST_DISPATCH_TIME
; }
73 void resetGyroTaskTestFlags(void) {
75 taskFilterRan
= false;
77 taskFilterReady
= false;
81 extern int taskQueueSize
;
82 extern task_t
* taskQueueArray
[];
84 extern void queueClear(void);
85 extern bool queueContains(task_t
*task
);
86 extern bool queueAdd(task_t
*task
);
87 extern bool queueRemove(task_t
*task
);
88 extern task_t
*queueFirst(void);
89 extern task_t
*queueNext(void);
91 task_t tasks
[TASK_COUNT
] = {
94 .taskFunc
= taskSystemLoad
,
95 .desiredPeriodUs
= TASK_PERIOD_HZ(10),
96 .staticPriority
= TASK_PRIORITY_MEDIUM_HIGH
,
100 .taskFunc
= taskGyroSample
,
101 .desiredPeriodUs
= TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ
),
102 .staticPriority
= TASK_PRIORITY_REALTIME
,
105 .taskName
= "FILTER",
106 .taskFunc
= taskFiltering
,
107 .desiredPeriodUs
= TASK_PERIOD_HZ(4000),
108 .staticPriority
= TASK_PRIORITY_REALTIME
,
112 .taskFunc
= taskMainPidLoop
,
113 .desiredPeriodUs
= TASK_PERIOD_HZ(4000),
114 .staticPriority
= TASK_PRIORITY_REALTIME
,
118 .taskFunc
= taskUpdateAccelerometer
,
119 .desiredPeriodUs
= TASK_PERIOD_HZ(1000),
120 .staticPriority
= TASK_PRIORITY_MEDIUM
,
123 .taskName
= "ATTITUDE",
124 .taskFunc
= imuUpdateAttitude
,
125 .desiredPeriodUs
= TASK_PERIOD_HZ(100),
126 .staticPriority
= TASK_PRIORITY_MEDIUM
,
130 .checkFunc
= rxUpdateCheck
,
131 .taskFunc
= taskUpdateRxMain
,
132 .desiredPeriodUs
= TASK_PERIOD_HZ(50),
133 .staticPriority
= TASK_PRIORITY_HIGH
,
136 .taskName
= "SERIAL",
137 .taskFunc
= taskHandleSerial
,
138 .desiredPeriodUs
= TASK_PERIOD_HZ(100),
139 .staticPriority
= TASK_PRIORITY_LOW
,
142 .taskName
= "DISPATCH",
143 .taskFunc
= dispatchProcess
,
144 .desiredPeriodUs
= TASK_PERIOD_HZ(1000),
145 .staticPriority
= TASK_PRIORITY_HIGH
,
147 [TASK_BATTERY_VOLTAGE
] = {
148 .taskName
= "BATTERY_VOLTAGE",
149 .taskFunc
= taskUpdateBatteryVoltage
,
150 .desiredPeriodUs
= TASK_PERIOD_HZ(50),
151 .staticPriority
= TASK_PRIORITY_MEDIUM
,
155 task_t
*getTask(unsigned taskId
)
157 return &tasks
[taskId
];
161 TEST(SchedulerUnittest
, TestPriorites
)
163 EXPECT_EQ(TASK_PRIORITY_MEDIUM_HIGH
, tasks
[TASK_SYSTEM
].staticPriority
);
164 EXPECT_EQ(TASK_PRIORITY_REALTIME
, tasks
[TASK_GYRO
].staticPriority
);
165 EXPECT_EQ(TASK_PRIORITY_MEDIUM
, tasks
[TASK_ACCEL
].staticPriority
);
166 EXPECT_EQ(TASK_PRIORITY_LOW
, tasks
[TASK_SERIAL
].staticPriority
);
167 EXPECT_EQ(TASK_PRIORITY_MEDIUM
, tasks
[TASK_BATTERY_VOLTAGE
].staticPriority
);
170 TEST(SchedulerUnittest
, TestQueueInit
)
173 EXPECT_EQ(0, taskQueueSize
);
174 EXPECT_EQ(0, queueFirst());
175 EXPECT_EQ(0, queueNext());
176 for (int ii
= 0; ii
<= TASK_COUNT
; ++ii
) {
177 EXPECT_EQ(0, taskQueueArray
[ii
]);
181 task_t
*deadBeefPtr
= reinterpret_cast<task_t
*>(0xDEADBEEF);
183 TEST(SchedulerUnittest
, TestQueue
)
186 taskQueueArray
[TASK_COUNT
+ 1] = deadBeefPtr
;
188 queueAdd(&tasks
[TASK_SYSTEM
]); // TASK_PRIORITY_MEDIUM_HIGH
189 EXPECT_EQ(1, taskQueueSize
);
190 EXPECT_EQ(&tasks
[TASK_SYSTEM
], queueFirst());
191 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT
+ 1]);
193 queueAdd(&tasks
[TASK_SERIAL
]); // TASK_PRIORITY_LOW
194 EXPECT_EQ(2, taskQueueSize
);
195 EXPECT_EQ(&tasks
[TASK_SYSTEM
], queueFirst());
196 EXPECT_EQ(&tasks
[TASK_SERIAL
], queueNext());
197 EXPECT_EQ(NULL
, queueNext());
198 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT
+ 1]);
200 queueAdd(&tasks
[TASK_BATTERY_VOLTAGE
]); // TASK_PRIORITY_MEDIUM
201 EXPECT_EQ(3, taskQueueSize
);
202 EXPECT_EQ(&tasks
[TASK_SYSTEM
], queueFirst());
203 EXPECT_EQ(&tasks
[TASK_BATTERY_VOLTAGE
], queueNext());
204 EXPECT_EQ(&tasks
[TASK_SERIAL
], queueNext());
205 EXPECT_EQ(NULL
, queueNext());
206 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT
+ 1]);
208 queueAdd(&tasks
[TASK_RX
]); // TASK_PRIORITY_HIGH
209 EXPECT_EQ(4, taskQueueSize
);
210 EXPECT_EQ(&tasks
[TASK_RX
], queueFirst());
211 EXPECT_EQ(&tasks
[TASK_SYSTEM
], queueNext());
212 EXPECT_EQ(&tasks
[TASK_BATTERY_VOLTAGE
], queueNext());
213 EXPECT_EQ(&tasks
[TASK_SERIAL
], queueNext());
214 EXPECT_EQ(NULL
, queueNext());
215 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT
+ 1]);
217 queueRemove(&tasks
[TASK_SYSTEM
]); // TASK_PRIORITY_HIGH
218 EXPECT_EQ(3, taskQueueSize
);
219 EXPECT_EQ(&tasks
[TASK_RX
], queueFirst());
220 EXPECT_EQ(&tasks
[TASK_BATTERY_VOLTAGE
], queueNext());
221 EXPECT_EQ(&tasks
[TASK_SERIAL
], queueNext());
222 EXPECT_EQ(NULL
, queueNext());
223 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT
+ 1]);
226 TEST(SchedulerUnittest
, TestQueueAddAndRemove
)
229 taskQueueArray
[TASK_COUNT
+ 1] = deadBeefPtr
;
232 for (int taskId
= 0; taskId
< TASK_COUNT
; ++taskId
) {
233 const bool added
= queueAdd(&tasks
[taskId
]);
235 EXPECT_EQ(taskId
+ 1, taskQueueSize
);
236 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT
+ 1]);
239 // double check end of queue
240 EXPECT_EQ(TASK_COUNT
, taskQueueSize
);
241 EXPECT_NE(static_cast<task_t
*>(0), taskQueueArray
[TASK_COUNT
- 1]); // last item was indeed added to queue
242 EXPECT_EQ(NULL
, taskQueueArray
[TASK_COUNT
]); // null pointer at end of queue is preserved
243 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT
+ 1]); // there hasn't been an out by one error
245 // and empty it again
246 for (int taskId
= 0; taskId
< TASK_COUNT
; ++taskId
) {
247 const bool removed
= queueRemove(&tasks
[taskId
]);
248 EXPECT_TRUE(removed
);
249 EXPECT_EQ(TASK_COUNT
- taskId
- 1, taskQueueSize
);
250 EXPECT_EQ(NULL
, taskQueueArray
[TASK_COUNT
- taskId
]);
251 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT
+ 1]);
254 // double check size and end of queue
255 EXPECT_EQ(0, taskQueueSize
); // queue is indeed empty
256 EXPECT_EQ(NULL
, taskQueueArray
[0]); // there is a null pointer at the end of the queueu
257 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT
+ 1]); // no accidental overwrites past end of queue
260 TEST(SchedulerUnittest
, TestQueueArray
)
262 // test there are no "out by one" errors or buffer overruns when items are added and removed
264 taskQueueArray
[TASK_COUNT_UNITTEST
+ 1] = deadBeefPtr
; // note, must set deadBeefPtr after queueClear
266 unsigned enqueuedTasks
= 0;
267 EXPECT_EQ(enqueuedTasks
, taskQueueSize
);
269 for (int taskId
= 0; taskId
< TASK_COUNT_UNITTEST
- 1; ++taskId
) {
270 if (tasks
[taskId
].taskFunc
) {
271 setTaskEnabled(static_cast<taskId_e
>(taskId
), true);
273 EXPECT_EQ(enqueuedTasks
, taskQueueSize
);
274 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT_UNITTEST
+ 1]);
278 EXPECT_NE(static_cast<task_t
*>(0), taskQueueArray
[enqueuedTasks
- 1]);
279 const task_t
*lastTaskPrev
= taskQueueArray
[enqueuedTasks
- 1];
280 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
]);
281 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
+ 1]);
282 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT_UNITTEST
+ 1]);
284 setTaskEnabled(TASK_SYSTEM
, false);
285 EXPECT_EQ(enqueuedTasks
- 1, taskQueueSize
);
286 EXPECT_EQ(lastTaskPrev
, taskQueueArray
[enqueuedTasks
- 2]);
287 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
- 1]); // NULL at end of queue
288 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
]);
289 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
+ 1]);
290 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT_UNITTEST
+ 1]);
292 taskQueueArray
[enqueuedTasks
- 1] = 0;
293 setTaskEnabled(TASK_SYSTEM
, true);
294 EXPECT_EQ(enqueuedTasks
, taskQueueSize
);
295 EXPECT_EQ(lastTaskPrev
, taskQueueArray
[enqueuedTasks
- 1]);
296 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
]);
297 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
+ 1]);
298 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT_UNITTEST
+ 1]);
301 getTaskInfo(static_cast<taskId_e
>(enqueuedTasks
+ 1), &taskInfo
);
302 EXPECT_FALSE(taskInfo
.isEnabled
);
303 setTaskEnabled(static_cast<taskId_e
>(enqueuedTasks
), true);
304 EXPECT_EQ(enqueuedTasks
, taskQueueSize
);
305 EXPECT_EQ(lastTaskPrev
, taskQueueArray
[enqueuedTasks
- 1]);
306 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
+ 1]); // check no buffer overrun
307 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT_UNITTEST
+ 1]);
309 setTaskEnabled(TASK_SYSTEM
, false);
310 EXPECT_EQ(enqueuedTasks
- 1, taskQueueSize
);
311 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
]);
312 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
+ 1]);
313 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT_UNITTEST
+ 1]);
315 setTaskEnabled(TASK_ACCEL
, false);
316 EXPECT_EQ(enqueuedTasks
- 2, taskQueueSize
);
317 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
- 1]);
318 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
]);
319 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
+ 1]);
320 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT_UNITTEST
+ 1]);
322 setTaskEnabled(TASK_BATTERY_VOLTAGE
, false);
323 EXPECT_EQ(enqueuedTasks
- 2, taskQueueSize
);
324 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
- 2]);
325 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
- 1]);
326 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
]);
327 EXPECT_EQ(NULL
, taskQueueArray
[enqueuedTasks
+ 1]);
328 EXPECT_EQ(deadBeefPtr
, taskQueueArray
[TASK_COUNT_UNITTEST
+ 1]);
331 TEST(SchedulerUnittest
, TestSchedulerInit
)
334 EXPECT_EQ(1, taskQueueSize
);
335 EXPECT_EQ(&tasks
[TASK_SYSTEM
], queueFirst());
338 TEST(SchedulerUnittest
, TestScheduleEmptyQueue
)
341 simulatedTime
= 4000;
342 // run the with an empty queue
344 EXPECT_EQ(NULL
, unittest_scheduler_selectedTask
);
347 TEST(SchedulerUnittest
, TestSingleTask
)
350 // disable all tasks except TASK_ACCEL
351 for (int taskId
= 0; taskId
< TASK_COUNT
; ++taskId
) {
352 setTaskEnabled(static_cast<taskId_e
>(taskId
), false);
354 setTaskEnabled(TASK_ACCEL
, true);
355 tasks
[TASK_ACCEL
].lastExecutedAtUs
= 1000;
356 tasks
[TASK_ACCEL
].lastStatsAtUs
= 1000;
357 simulatedTime
= 2050;
358 // run the scheduler and check the task has executed
360 EXPECT_NE(unittest_scheduler_selectedTask
, static_cast<task_t
*>(0));
361 EXPECT_EQ(unittest_scheduler_selectedTask
, &tasks
[TASK_ACCEL
]);
362 EXPECT_EQ(1050, tasks
[TASK_ACCEL
].taskLatestDeltaTimeUs
);
363 EXPECT_EQ(2050, tasks
[TASK_ACCEL
].lastExecutedAtUs
);
364 EXPECT_EQ(TEST_UPDATE_ACCEL_TIME
, tasks
[TASK_ACCEL
].totalExecutionTimeUs
);
365 // task has run, so its dynamic priority should have been set to zero
366 EXPECT_EQ(0, tasks
[TASK_GYRO
].dynamicPriority
);
369 TEST(SchedulerUnittest
, TestTwoTasks
)
371 // disable all tasks except TASK_ACCEL and TASK_ATTITUDE
372 for (int taskId
= 0; taskId
< TASK_COUNT
; ++taskId
) {
373 setTaskEnabled(static_cast<taskId_e
>(taskId
), false);
375 setTaskEnabled(TASK_ACCEL
, true);
376 setTaskEnabled(TASK_ATTITUDE
, true);
378 // set it up so that TASK_ACCEL ran just before TASK_ATTITUDE
379 static const uint32_t startTime
= 4000;
380 simulatedTime
= startTime
;
381 tasks
[TASK_ACCEL
].lastExecutedAtUs
= simulatedTime
;
382 tasks
[TASK_ATTITUDE
].lastExecutedAtUs
= tasks
[TASK_ACCEL
].lastExecutedAtUs
- TEST_UPDATE_ATTITUDE_TIME
;
383 EXPECT_EQ(0, tasks
[TASK_ATTITUDE
].taskAgeCycles
);
386 // no tasks should have run, since neither task's desired time has elapsed
387 EXPECT_EQ(static_cast<task_t
*>(0), unittest_scheduler_selectedTask
);
390 // TASK_ACCEL desiredPeriodUs is 1000 microseconds
391 // TASK_ATTITUDE desiredPeriodUs is 10000 microseconds
392 // 500 microseconds later
393 simulatedTime
+= 500;
394 // no tasks should run, since neither task's desired time has elapsed
396 EXPECT_EQ(static_cast<task_t
*>(0), unittest_scheduler_selectedTask
);
397 EXPECT_EQ(0, unittest_scheduler_waitingTasks
);
399 // 500 microseconds later, TASK_ACCEL desiredPeriodUs has elapsed
400 simulatedTime
+= 500;
401 // TASK_ACCEL should now run
403 EXPECT_EQ(&tasks
[TASK_ACCEL
], unittest_scheduler_selectedTask
);
404 EXPECT_EQ(1, unittest_scheduler_waitingTasks
);
405 EXPECT_EQ(5000 + TEST_UPDATE_ACCEL_TIME
, simulatedTime
);
407 simulatedTime
+= 1000 - TEST_UPDATE_ACCEL_TIME
;
409 // TASK_ACCEL should run again
410 EXPECT_EQ(&tasks
[TASK_ACCEL
], unittest_scheduler_selectedTask
);
413 // No task should have run
414 EXPECT_EQ(static_cast<task_t
*>(0), unittest_scheduler_selectedTask
);
415 EXPECT_EQ(0, unittest_scheduler_waitingTasks
);
417 simulatedTime
= startTime
+ 10500; // TASK_ACCEL and TASK_ATTITUDE desiredPeriodUss have elapsed
418 // of the two TASK_ACCEL should run first
420 EXPECT_EQ(&tasks
[TASK_ACCEL
], unittest_scheduler_selectedTask
);
421 // and finally TASK_ATTITUDE should now run
423 EXPECT_EQ(&tasks
[TASK_ATTITUDE
], unittest_scheduler_selectedTask
);
426 TEST(SchedulerUnittest
, TestGyroTask
)
428 static const uint32_t startTime
= 4000;
431 schedulerEnableGyro();
433 // disable all tasks except TASK_GYRO, TASK_FILTER and TASK_PID
434 for (int taskId
= 0; taskId
< TASK_COUNT
; ++taskId
) {
435 setTaskEnabled(static_cast<taskId_e
>(taskId
), false);
437 setTaskEnabled(TASK_GYRO
, true);
438 setTaskEnabled(TASK_FILTER
, true);
439 setTaskEnabled(TASK_PID
, true);
441 // First set it up so TASK_GYRO just ran
442 simulatedTime
= startTime
;
443 tasks
[TASK_GYRO
].lastExecutedAtUs
= simulatedTime
;
445 resetGyroTaskTestFlags();
449 // no tasks should have run
450 EXPECT_EQ(static_cast<task_t
*>(0), unittest_scheduler_selectedTask
);
451 // also the gyro, filter and PID task indicators should be false
452 EXPECT_FALSE(taskGyroRan
);
453 EXPECT_FALSE(taskFilterRan
);
454 EXPECT_FALSE(taskPidRan
);
456 /* Test the gyro task running but not triggering the filtering or PID */
457 // set the TASK_GYRO last executed time to be one period earlier
458 simulatedTime
= startTime
;
459 tasks
[TASK_GYRO
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ
);
462 resetGyroTaskTestFlags();
466 // the gyro task indicator should be true and the TASK_FILTER and TASK_PID indicators should be false
467 EXPECT_TRUE(taskGyroRan
);
468 EXPECT_FALSE(taskFilterRan
);
469 EXPECT_FALSE(taskPidRan
);
470 // expect that no other tasks other than TASK_GYRO should have run
471 EXPECT_EQ(static_cast<task_t
*>(0), unittest_scheduler_selectedTask
);
473 /* Test the gyro task running and triggering the filtering task */
474 // set the TASK_GYRO last executed time to be one period earlier
475 simulatedTime
= startTime
;
476 tasks
[TASK_GYRO
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ
);
479 resetGyroTaskTestFlags();
480 taskFilterReady
= true;
484 // the gyro and filter task indicators should be true and TASK_PID indicator should be false
485 EXPECT_TRUE(taskGyroRan
);
486 EXPECT_TRUE(taskFilterRan
);
487 EXPECT_FALSE(taskPidRan
);
488 // expect that no other tasks other tasks should have run
489 EXPECT_EQ(static_cast<task_t
*>(0), unittest_scheduler_selectedTask
);
491 /* Test the gyro task running and triggering the PID task */
492 // set the TASK_GYRO last executed time to be one period earlier
493 simulatedTime
= startTime
;
494 tasks
[TASK_GYRO
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ
);
497 resetGyroTaskTestFlags();
502 // the gyro and PID task indicators should be true and TASK_FILTER indicator should be false
503 EXPECT_TRUE(taskGyroRan
);
504 EXPECT_FALSE(taskFilterRan
);
505 EXPECT_TRUE(taskPidRan
);
506 // expect that no other tasks other tasks should have run
507 EXPECT_EQ(static_cast<task_t
*>(0), unittest_scheduler_selectedTask
);
510 // Test the scheduling logic that prevents other tasks from running if they
511 // might interfere with the timing of the next gyro task.
512 TEST(SchedulerUnittest
, TestGyroLookahead
)
514 static const uint32_t startTime
= 4000;
516 // enable task statistics
517 schedulerSetCalulateTaskStatistics(true);
519 // disable scheduler optimize rate
520 schedulerOptimizeRate(false);
523 schedulerEnableGyro();
525 // disable all tasks except TASK_GYRO, TASK_ACCEL
526 for (int taskId
= 0; taskId
< TASK_COUNT
; ++taskId
) {
527 setTaskEnabled(static_cast<taskId_e
>(taskId
), false);
529 setTaskEnabled(TASK_GYRO
, true);
530 setTaskEnabled(TASK_ACCEL
, true);
532 #if defined(USE_TASK_STATISTICS)
533 // set the average run time for TASK_ACCEL
534 tasks
[TASK_ACCEL
].movingSumExecutionTimeUs
= TEST_UPDATE_ACCEL_TIME
* TASK_STATS_MOVING_SUM_COUNT
;
537 /* Test that another task will run if there's plenty of time till the next gyro sample time */
538 // set it up so TASK_GYRO just ran and TASK_ACCEL is ready to run
539 simulatedTime
= startTime
;
540 tasks
[TASK_GYRO
].lastExecutedAtUs
= simulatedTime
;
541 tasks
[TASK_ACCEL
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(1000);
543 resetGyroTaskTestFlags();
547 // the gyro, filter and PID task indicators should be false
548 EXPECT_FALSE(taskGyroRan
);
549 EXPECT_FALSE(taskFilterRan
);
550 EXPECT_FALSE(taskPidRan
);
551 // TASK_ACCEL should have run
552 EXPECT_EQ(&tasks
[TASK_ACCEL
], unittest_scheduler_selectedTask
);
554 /* Test that another task won't run if the time till the gyro task is less than the guard interval */
555 // set it up so TASK_GYRO will run soon and TASK_ACCEL is ready to run
556 simulatedTime
= startTime
;
557 tasks
[TASK_GYRO
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ
) + GYRO_TASK_GUARD_INTERVAL_US
/ 2;
558 tasks
[TASK_ACCEL
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(1000);
560 resetGyroTaskTestFlags();
564 // the gyro, filter and PID task indicators should be false
565 EXPECT_FALSE(taskGyroRan
);
566 EXPECT_FALSE(taskFilterRan
);
567 EXPECT_FALSE(taskPidRan
);
568 // TASK_ACCEL should not have run
569 EXPECT_EQ(static_cast<task_t
*>(0), unittest_scheduler_selectedTask
);
571 /* Test that another task won't run if the time till the gyro task is less than the average task interval */
572 // set it up so TASK_GYRO will run soon and TASK_ACCEL is ready to run
573 simulatedTime
= startTime
;
574 tasks
[TASK_GYRO
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ
) + TEST_UPDATE_ACCEL_TIME
/ 2;
575 tasks
[TASK_ACCEL
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(1000);
577 resetGyroTaskTestFlags();
581 // the gyro, filter and PID task indicators should be false
582 EXPECT_FALSE(taskGyroRan
);
583 EXPECT_FALSE(taskFilterRan
);
584 EXPECT_FALSE(taskPidRan
);
585 // TASK_ACCEL should not have run
586 EXPECT_EQ(static_cast<task_t
*>(0), unittest_scheduler_selectedTask
);
588 /* Test that another task will run if the gyro task gets executed */
589 // set it up so TASK_GYRO will run now and TASK_ACCEL is ready to run
590 simulatedTime
= startTime
;
591 tasks
[TASK_GYRO
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(TEST_GYRO_SAMPLE_HZ
);
592 tasks
[TASK_ACCEL
].lastExecutedAtUs
= simulatedTime
- TASK_PERIOD_HZ(1000);
594 resetGyroTaskTestFlags();
596 // make the TASK_FILTER and TASK_PID ready to run
597 taskFilterReady
= true;
602 // TASK_GYRO, TASK_FILTER, and TASK_PID should all run
603 EXPECT_TRUE(taskGyroRan
);
604 EXPECT_TRUE(taskFilterRan
);
605 EXPECT_TRUE(taskPidRan
);
606 // TASK_ACCEL should have run
607 EXPECT_EQ(&tasks
[TASK_ACCEL
], unittest_scheduler_selectedTask
);