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.h"
25 #include "unittest_macros.h"
26 #include "gtest/gtest.h"
29 pidLoopCheckerTime = 650,
30 updateAccelerometerTime = 192,
31 handleSerialTime = 30,
33 updateBatteryTime = 1,
34 updateRxCheckTime = 34,
35 updateRxMainTime = 10,
37 updateCompassTime = 195,
40 calculateAltitudeTime = 154,
41 updateDisplayTime = 10,
48 cfTask_t * unittest_scheduler_selectedTask;
49 uint8_t unittest_scheduler_selectedTaskDynPrio;
50 uint16_t unittest_scheduler_waitingTasks;
51 uint32_t unittest_scheduler_timeToNextRealtimeTask;
52 bool unittest_outsideRealtimeGuardInterval;
54 // set up micros() to simulate time
55 uint32_t simulatedTime = 0;
56 uint32_t micros(void) {return simulatedTime;}
57 // set up tasks to take a simulated representative time to execute
58 void taskMainPidLoopChecker(void) {simulatedTime+=pidLoopCheckerTime;}
59 void taskUpdateAccelerometer(void) {simulatedTime+=updateAccelerometerTime;}
60 void taskHandleSerial(void) {simulatedTime+=handleSerialTime;}
61 void taskUpdateBeeper(void) {simulatedTime+=updateBeeperTime;}
62 void taskUpdateBattery(void) {simulatedTime+=updateBatteryTime;}
63 bool taskUpdateRxCheck(uint32_t currentDeltaTime) {UNUSED(currentDeltaTime);simulatedTime+=updateRxCheckTime;return false;}
64 void taskUpdateRxMain(void) {simulatedTime+=updateRxMainTime;}
65 void taskProcessGPS(void) {simulatedTime+=processGPSTime;}
66 void taskUpdateCompass(void) {simulatedTime+=updateCompassTime;}
67 void taskUpdateBaro(void) {simulatedTime+=updateBaroTime;}
68 void taskUpdateRangefinder(void) {simulatedTime+=updateSonarTime;}
69 void taskCalculateAltitude(void) {simulatedTime+=calculateAltitudeTime;}
70 void taskUpdateDisplay(void) {simulatedTime+=updateDisplayTime;}
71 void taskTelemetry(void) {simulatedTime+=telemetryTime;}
72 void taskLedStrip(void) {simulatedTime+=ledStripTime;}
73 void taskTransponder(void) {simulatedTime+=transponderTime;}
75 extern cfTask_t* taskQueueArray[];
77 extern void queueClear(void);
78 extern int queueSize();
79 extern bool queueContains(cfTask_t *task);
80 extern bool queueAdd(cfTask_t *task);
81 extern bool queueRemove(cfTask_t *task);
82 extern cfTask_t *queueFirst(void);
83 extern cfTask_t *queueNext(void);
86 TEST(SchedulerUnittest, TestPriorites)
88 EXPECT_EQ(14, TASK_COUNT);
89 // if any of these fail then task priorities have changed and ordering in TestQueue needs to be re-checked
90 EXPECT_EQ(TASK_PRIORITY_HIGH, cfTasks[TASK_SYSTEM].staticPriority);
91 EXPECT_EQ(TASK_PRIORITY_REALTIME, cfTasks[TASK_PID].staticPriority);
92 EXPECT_EQ(TASK_PRIORITY_MEDIUM, cfTasks[TASK_ACCEL].staticPriority);
93 EXPECT_EQ(TASK_PRIORITY_LOW, cfTasks[TASK_SERIAL].staticPriority);
94 EXPECT_EQ(TASK_PRIORITY_MEDIUM, cfTasks[TASK_BATTERY].staticPriority);
97 TEST(SchedulerUnittest, TestQueueInit)
100 EXPECT_EQ(0, queueSize());
101 EXPECT_EQ(0, queueFirst());
102 EXPECT_EQ(0, queueNext());
103 for (int ii = 0; ii <= TASK_COUNT; ++ii) {
104 EXPECT_EQ(0, taskQueueArray[ii]);
108 cfTask_t *deadBeefPtr = reinterpret_cast<cfTask_t*>(0xDEADBEEF);
110 TEST(SchedulerUnittest, TestQueue)
113 taskQueueArray[TASK_COUNT + 1] = deadBeefPtr;
115 queueAdd(&cfTasks[TASK_SYSTEM]); // TASK_PRIORITY_HIGH
116 EXPECT_EQ(1, queueSize());
117 EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueFirst());
118 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
120 queueAdd(&cfTasks[TASK_PID]); // TASK_PRIORITY_REALTIME
121 EXPECT_EQ(2, queueSize());
122 EXPECT_EQ(&cfTasks[TASK_PID], queueFirst());
123 EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
124 EXPECT_EQ(NULL, queueNext());
125 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
127 queueAdd(&cfTasks[TASK_SERIAL]); // TASK_PRIORITY_LOW
128 EXPECT_EQ(3, queueSize());
129 EXPECT_EQ(&cfTasks[TASK_PID], queueFirst());
130 EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
131 EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
132 EXPECT_EQ(NULL, queueNext());
133 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
135 queueAdd(&cfTasks[TASK_BEEPER]); // TASK_PRIORITY_MEDIUM
136 EXPECT_EQ(4, queueSize());
137 EXPECT_EQ(&cfTasks[TASK_PID], queueFirst());
138 EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
139 EXPECT_EQ(&cfTasks[TASK_BEEPER], queueNext());
140 EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
141 EXPECT_EQ(NULL, queueNext());
142 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
144 queueAdd(&cfTasks[TASK_RX]); // TASK_PRIORITY_HIGH
145 EXPECT_EQ(5, queueSize());
146 EXPECT_EQ(&cfTasks[TASK_PID], queueFirst());
147 EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
148 EXPECT_EQ(&cfTasks[TASK_RX], queueNext());
149 EXPECT_EQ(&cfTasks[TASK_BEEPER], queueNext());
150 EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
151 EXPECT_EQ(NULL, queueNext());
152 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
154 queueRemove(&cfTasks[TASK_SYSTEM]); // TASK_PRIORITY_HIGH
155 EXPECT_EQ(4, queueSize());
156 EXPECT_EQ(&cfTasks[TASK_PID], queueFirst());
157 EXPECT_EQ(&cfTasks[TASK_RX], queueNext());
158 EXPECT_EQ(&cfTasks[TASK_BEEPER], queueNext());
159 EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
160 EXPECT_EQ(NULL, queueNext());
163 TEST(SchedulerUnittest, TestQueueAddAndRemove)
166 taskQueueArray[TASK_COUNT + 1] = deadBeefPtr;
169 for (int taskId = 0; taskId < TASK_COUNT; ++taskId) {
170 const bool added = queueAdd(&cfTasks[taskId]);
171 EXPECT_EQ(true, added);
172 EXPECT_EQ(taskId + 1, queueSize());
173 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
175 // double check end of queue
176 EXPECT_EQ(TASK_COUNT, queueSize());
177 EXPECT_NE(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 1]); // last item was indeed added to queue
178 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]); // null pointer at end of queue is preserved
179 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]); // there hasn't been an out by one error
181 // and empty it again
182 for (int taskId = 0; taskId < TASK_COUNT; ++taskId) {
183 const bool removed = queueRemove(&cfTasks[taskId]);
184 EXPECT_EQ(true, removed);
185 EXPECT_EQ(TASK_COUNT - taskId - 1, queueSize());
186 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - taskId]);
187 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
189 // double check size and end of queue
190 EXPECT_EQ(0, queueSize()); // queue is indeed empty
191 EXPECT_EQ(NULL, taskQueueArray[0]); // there is a null pointer at the end of the queueu
192 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]); // no accidental overwrites past end of queue
195 TEST(SchedulerUnittest, TestQueueArray)
197 // test there are no "out by one" errors or buffer overruns when items are added and removed
199 taskQueueArray[TASK_COUNT + 1] = deadBeefPtr;
201 for (int taskId=0; taskId < TASK_COUNT - 1; ++taskId) {
202 setTaskEnabled(static_cast<cfTaskId_e>(taskId), true);
204 EXPECT_EQ(TASK_COUNT - 1, queueSize());
205 EXPECT_NE(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 2]);
206 const cfTask_t *lastTaskPrev = taskQueueArray[TASK_COUNT - 2];
207 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
208 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
209 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
211 setTaskEnabled(TASK_SYSTEM, false);
212 EXPECT_EQ(TASK_COUNT - 2, queueSize());
213 EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 3]);
214 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 2]); // NULL at end of queue
215 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
216 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
217 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
219 taskQueueArray[TASK_COUNT - 2] = 0;
220 setTaskEnabled(TASK_SYSTEM, true);
221 EXPECT_EQ(TASK_COUNT - 1, queueSize());
222 EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 2]);
223 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
224 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
225 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
227 cfTaskInfo_t taskInfo;
228 getTaskInfo(static_cast<cfTaskId_e>(TASK_COUNT - 1), &taskInfo);
229 EXPECT_EQ(false, taskInfo.isEnabled);
230 setTaskEnabled(static_cast<cfTaskId_e>(TASK_COUNT - 1), true);
231 EXPECT_EQ(TASK_COUNT, queueSize());
232 EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 1]);
233 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]); // check no buffer overrun
234 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
236 setTaskEnabled(TASK_SYSTEM, false);
237 EXPECT_EQ(TASK_COUNT - 1, queueSize());
238 //EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 3]);
239 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
240 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
241 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
243 setTaskEnabled(TASK_ACCEL, false);
244 EXPECT_EQ(TASK_COUNT - 2, queueSize());
245 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 2]);
246 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
247 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
248 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
250 setTaskEnabled(TASK_BATTERY, false);
251 EXPECT_EQ(TASK_COUNT - 3, queueSize());
252 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 3]);
253 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 2]);
254 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
255 EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
256 EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
259 TEST(SchedulerUnittest, TestSchedulerInit)
262 EXPECT_EQ(1, queueSize());
263 EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueFirst());
266 TEST(SchedulerUnittest, TestScheduleEmptyQueue)
269 simulatedTime = 4000;
270 // run the with an empty queue
272 EXPECT_EQ(NULL, unittest_scheduler_selectedTask);
275 TEST(SchedulerUnittest, TestSingleTask)
278 // disable all tasks except TASK_PID
279 for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
280 setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
282 setTaskEnabled(TASK_PID, true);
283 cfTasks[TASK_PID].lastExecutedAt = 1000;
284 simulatedTime = 4000;
285 // run the scheduler and check the task has executed
287 EXPECT_NE(static_cast<cfTask_t*>(0), unittest_scheduler_selectedTask);
288 EXPECT_EQ(&cfTasks[TASK_PID], unittest_scheduler_selectedTask);
289 EXPECT_EQ(3000, cfTasks[TASK_PID].taskLatestDeltaTime);
290 EXPECT_EQ(4000, cfTasks[TASK_PID].lastExecutedAt);
291 EXPECT_EQ(pidLoopCheckerTime, cfTasks[TASK_PID].totalExecutionTime);
292 // task has run, so its dynamic priority should have been set to zero
293 EXPECT_EQ(0, cfTasks[TASK_PID].dynamicPriority);
296 TEST(SchedulerUnittest, TestTwoTasks)
298 // disable all tasks except TASK_PID and TASK_ACCEL
299 for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
300 setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
302 setTaskEnabled(TASK_ACCEL, true);
303 setTaskEnabled(TASK_PID, true);
305 // set it up so that TASK_ACCEL ran just before TASK_PID
306 static const uint32_t startTime = 4000;
307 simulatedTime = startTime;
308 cfTasks[TASK_PID].lastExecutedAt = simulatedTime;
309 cfTasks[TASK_ACCEL].lastExecutedAt = cfTasks[TASK_PID].lastExecutedAt - updateAccelerometerTime;
310 EXPECT_EQ(0, cfTasks[TASK_ACCEL].taskAgeCycles);
313 // no tasks should have run, since neither task's desired time has elapsed
314 EXPECT_EQ(static_cast<cfTask_t*>(0), unittest_scheduler_selectedTask);
317 // TASK_PID desiredPeriod is 1000 microseconds
318 // TASK_ACCEL desiredPeriod is 10000 microseconds
319 // 500 microseconds later
320 simulatedTime += 500;
321 // no tasks should run, since neither task's desired time has elapsed
323 EXPECT_EQ(static_cast<cfTask_t*>(0), unittest_scheduler_selectedTask);
324 EXPECT_EQ(0, unittest_scheduler_waitingTasks);
326 // 500 microseconds later, TASK_PID desiredPeriod has elapsed
327 simulatedTime += 500;
328 // TASK_PID should now run
330 EXPECT_EQ(&cfTasks[TASK_PID], unittest_scheduler_selectedTask);
331 EXPECT_EQ(1, unittest_scheduler_waitingTasks);
332 EXPECT_EQ(5000 + pidLoopCheckerTime, simulatedTime);
334 simulatedTime += 1000 - pidLoopCheckerTime;
336 // TASK_PID should run again
337 EXPECT_EQ(&cfTasks[TASK_PID], unittest_scheduler_selectedTask);
340 EXPECT_EQ(static_cast<cfTask_t*>(0), unittest_scheduler_selectedTask);
341 EXPECT_EQ(0, unittest_scheduler_waitingTasks);
343 simulatedTime = startTime + 10500; // TASK_PID and TASK_ACCEL desiredPeriods have elapsed
344 // of the two TASK_PID should run first
346 EXPECT_EQ(&cfTasks[TASK_PID], unittest_scheduler_selectedTask);
347 // and finally TASK_ACCEL should now run
349 EXPECT_EQ(&cfTasks[TASK_ACCEL], unittest_scheduler_selectedTask);
352 TEST(SchedulerUnittest, TestRealTimeGuardInNoTaskRun)
354 // disable all tasks except TASK_PID and TASK_SYSTEM
355 for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
356 setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
358 setTaskEnabled(TASK_PID, true);
359 cfTasks[TASK_PID].lastExecutedAt = 200000;
360 simulatedTime = 200700;
362 setTaskEnabled(TASK_SYSTEM, true);
363 cfTasks[TASK_SYSTEM].lastExecutedAt = 100000;
367 EXPECT_EQ(false, unittest_outsideRealtimeGuardInterval);
368 EXPECT_EQ(300, unittest_scheduler_timeToNextRealtimeTask);
370 // Nothing should be scheduled in guard period
371 EXPECT_EQ(NULL, unittest_scheduler_selectedTask);
372 EXPECT_EQ(100000, cfTasks[TASK_SYSTEM].lastExecutedAt);
374 EXPECT_EQ(200000, cfTasks[TASK_PID].lastExecutedAt);
377 TEST(SchedulerUnittest, TestRealTimeGuardOutTaskRun)
379 // disable all tasks except TASK_PID and TASK_SYSTEM
380 for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
381 setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
383 setTaskEnabled(TASK_PID, true);
384 cfTasks[TASK_PID].lastExecutedAt = 200000;
385 simulatedTime = 200699;
387 setTaskEnabled(TASK_SYSTEM, true);
388 cfTasks[TASK_SYSTEM].lastExecutedAt = 100000;
392 EXPECT_EQ(true, unittest_outsideRealtimeGuardInterval);
393 EXPECT_EQ(301, unittest_scheduler_timeToNextRealtimeTask);
395 // System should be scheduled as not in guard period
396 EXPECT_EQ(&cfTasks[TASK_SYSTEM], unittest_scheduler_selectedTask);
397 EXPECT_EQ(200699, cfTasks[TASK_SYSTEM].lastExecutedAt);
399 EXPECT_EQ(200000, cfTasks[TASK_PID].lastExecutedAt);