commence breakage
[inav.git] / src / test / unit / scheduler_unittest.cc.txt
blobff8c4cdb0ea2f2e4567b53dac698f90fa575c783
1 /*
2  * This file is part of Cleanflight.
3  *
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.
8  *
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.
13  *
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/>.
16  */
18 #include <stdint.h>
20 extern "C" {
21     #include "platform.h"
22     #include "scheduler.h"
25 #include "unittest_macros.h"
26 #include "gtest/gtest.h"
27 enum {
28     systemTime = 10,
29     pidLoopCheckerTime = 650,
30     updateAccelerometerTime = 192,
31     handleSerialTime = 30,
32     updateBeeperTime = 1,
33     updateBatteryTime = 1,
34     updateRxCheckTime = 34,
35     updateRxMainTime = 10,
36     processGPSTime = 10,
37     updateCompassTime = 195,
38     updateBaroTime = 201,
39     updateSonarTime = 10,
40     calculateAltitudeTime = 154,
41     updateDisplayTime = 10,
42     telemetryTime = 10,
43     ledStripTime = 10,
44     transponderTime = 10
47 extern "C" {
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)
99     queueClear();
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]);
105     }
108 cfTask_t *deadBeefPtr = reinterpret_cast<cfTask_t*>(0xDEADBEEF);
110 TEST(SchedulerUnittest, TestQueue)
112     queueClear();
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)
165     queueClear();
166     taskQueueArray[TASK_COUNT + 1] = deadBeefPtr;
168     // fill up the queue
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]);
174     }
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]);
188     }
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
198     queueClear();
199     taskQueueArray[TASK_COUNT + 1] = deadBeefPtr;
201     for (int taskId=0; taskId < TASK_COUNT - 1; ++taskId) {
202         setTaskEnabled(static_cast<cfTaskId_e>(taskId), true);
203     }
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)
261     schedulerInit();
262     EXPECT_EQ(1, queueSize());
263     EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueFirst());
266 TEST(SchedulerUnittest, TestScheduleEmptyQueue)
268     queueClear();
269     simulatedTime = 4000;
270     // run the with an empty queue
271     scheduler();
272     EXPECT_EQ(NULL, unittest_scheduler_selectedTask);
275 TEST(SchedulerUnittest, TestSingleTask)
277     schedulerInit();
278     // disable all tasks except TASK_PID
279     for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
280         setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
281     }
282     setTaskEnabled(TASK_PID, true);
283     cfTasks[TASK_PID].lastExecutedAt = 1000;
284     simulatedTime = 4000;
285     // run the scheduler and check the task has executed
286     scheduler();
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);
301     }
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);
311     // run the scheduler
312     scheduler();
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);
316     // NOTE:
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
322     scheduler();
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
329     scheduler();
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;
335     scheduler();
336     // TASK_PID should run again
337     EXPECT_EQ(&cfTasks[TASK_PID], unittest_scheduler_selectedTask);
339     scheduler();
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
345     scheduler();
346     EXPECT_EQ(&cfTasks[TASK_PID], unittest_scheduler_selectedTask);
347     // and finally TASK_ACCEL should now run
348     scheduler();
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);
357     }
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;
365     scheduler();
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);
382     }
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;
390     scheduler();
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);
402 // STUBS
403 extern "C" {