SimpleKernel 1.17.0
Loading...
Searching...
No Matches
idle_scheduler_test.cpp
Go to the documentation of this file.
1
5#include "idle_scheduler.hpp"
6
7#include <cstdint>
8
9#include "system_test.h"
11#include "task_messages.hpp"
12
13namespace {
14
15auto test_idle_basic_functionality() -> bool {
16 klog::Info("Running test_idle_basic_functionality...");
17
18 IdleScheduler scheduler;
19
20 // 验证调度器名称
21 EXPECT_EQ(scheduler.name[0], 'I', "Scheduler name should start with I");
22
23 // 创建 idle 任务
24 TaskControlBlock idle_task("IdleTask", 0, nullptr, nullptr);
26
27 // 测试空队列
28 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty initially");
29 EXPECT_EQ(scheduler.GetQueueSize(), 0,
30 "Queue size should be 0 for empty queue");
31 EXPECT_EQ(scheduler.PickNext(), nullptr,
32 "PickNext should return nullptr for empty queue");
33
34 // 加入 idle 任务
35 scheduler.Enqueue(&idle_task);
36 EXPECT_EQ(scheduler.GetQueueSize(), 1,
37 "Queue size should be 1 after enqueue");
38 EXPECT_FALSE(scheduler.IsEmpty(), "Scheduler should not be empty");
39
40 // PickNext 应返回 idle 任务
41 auto* picked = scheduler.PickNext();
42 EXPECT_EQ(picked, &idle_task, "PickNext should return idle task");
43
44 // 关键特性:PickNext 不应移除 idle 任务
45 EXPECT_FALSE(scheduler.IsEmpty(),
46 "Scheduler should NOT be empty after PickNext");
47 EXPECT_EQ(scheduler.GetQueueSize(), 1,
48 "Queue size should still be 1 after PickNext");
49
50 // 多次 PickNext 应始终返回同一个 idle 任务
51 auto* picked2 = scheduler.PickNext();
52 EXPECT_EQ(picked2, &idle_task,
53 "Second PickNext should return same idle task");
54 EXPECT_EQ(scheduler.GetQueueSize(), 1,
55 "Queue size should still be 1 after multiple PickNext");
56
57 klog::Info("test_idle_basic_functionality passed");
58 return true;
59}
60
61auto test_idle_pick_next_does_not_remove() -> bool {
62 klog::Info("Running test_idle_pick_next_does_not_remove...");
63
64 IdleScheduler scheduler;
65
66 TaskControlBlock idle_task("IdleTask", 0, nullptr, nullptr);
67 scheduler.Enqueue(&idle_task);
68
69 // PickNext 多次,任务始终存在
70 constexpr int kPickCount = 10;
71 for (int i = 0; i < kPickCount; ++i) {
72 auto* picked = scheduler.PickNext();
73 EXPECT_EQ(picked, &idle_task, "PickNext should always return idle task");
74 EXPECT_EQ(scheduler.GetQueueSize(), 1,
75 "Queue size should remain 1 after PickNext");
76 }
77
78 klog::Info("test_idle_pick_next_does_not_remove passed");
79 return true;
80}
81
82auto test_idle_enqueue_dequeue() -> bool {
83 klog::Info("Running test_idle_enqueue_dequeue...");
84
85 IdleScheduler scheduler;
86
87 TaskControlBlock idle_task("IdleTask", 0, nullptr, nullptr);
88
89 // 入队
90 scheduler.Enqueue(&idle_task);
91 EXPECT_EQ(scheduler.GetQueueSize(), 1, "Queue size should be 1");
92
93 // 出队
94 scheduler.Dequeue(&idle_task);
95 EXPECT_EQ(scheduler.GetQueueSize(), 0,
96 "Queue size should be 0 after dequeue");
97 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty after dequeue");
98 EXPECT_EQ(scheduler.PickNext(), nullptr,
99 "PickNext should return nullptr after dequeue");
100
101 // 重新入队
102 scheduler.Enqueue(&idle_task);
103 EXPECT_EQ(scheduler.GetQueueSize(), 1,
104 "Queue size should be 1 after re-enqueue");
105 EXPECT_EQ(scheduler.PickNext(), &idle_task,
106 "PickNext should return idle task after re-enqueue");
107
108 klog::Info("test_idle_enqueue_dequeue passed");
109 return true;
110}
111
112auto test_idle_on_tick_always_false() -> bool {
113 klog::Info("Running test_idle_on_tick_always_false...");
114
115 IdleScheduler scheduler;
116
117 TaskControlBlock idle_task("IdleTask", 0, nullptr, nullptr);
118 scheduler.Enqueue(&idle_task);
119
120 // OnTick 应始终返回 false(idle 不需要重新调度)
121 constexpr int kTickCount = 10;
122 for (int i = 0; i < kTickCount; ++i) {
123 bool need_resched = scheduler.OnTick(&idle_task);
124 EXPECT_FALSE(need_resched, "OnTick should always return false for idle");
125 }
126
127 klog::Info("test_idle_on_tick_always_false passed");
128 return true;
129}
130
131auto test_idle_on_time_slice_expired_always_false() -> bool {
132 klog::Info("Running test_idle_on_time_slice_expired_always_false...");
133
134 IdleScheduler scheduler;
135
136 TaskControlBlock idle_task("IdleTask", 0, nullptr, nullptr);
137 scheduler.Enqueue(&idle_task);
138
139 // OnTimeSliceExpired 应始终返回 false(idle 不使用时间片)
140 bool need_requeue = scheduler.OnTimeSliceExpired(&idle_task);
141 EXPECT_FALSE(need_requeue, "OnTimeSliceExpired should return false for idle");
142
143 klog::Info("test_idle_on_time_slice_expired_always_false passed");
144 return true;
145}
146
147auto test_idle_statistics() -> bool {
148 klog::Info("Running test_idle_statistics...");
149
150 IdleScheduler scheduler;
151
152 TaskControlBlock idle_task("IdleTask", 0, nullptr, nullptr);
153
154 // 初始统计
155 auto stats = scheduler.GetStats();
156 EXPECT_EQ(stats.total_enqueues, 0, "Initial enqueues should be 0");
157 EXPECT_EQ(stats.total_dequeues, 0, "Initial dequeues should be 0");
158 EXPECT_EQ(stats.total_picks, 0, "Initial picks should be 0");
159 EXPECT_EQ(stats.total_preemptions, 0, "Initial preemptions should be 0");
160
161 // 测试入队统计
162 scheduler.Enqueue(&idle_task);
163 stats = scheduler.GetStats();
164 EXPECT_EQ(stats.total_enqueues, 1, "Enqueues should be 1");
165
166 // 测试选择统计(PickNext 不移除)
167 (void)scheduler.PickNext();
168 (void)scheduler.PickNext();
169 stats = scheduler.GetStats();
170 EXPECT_EQ(stats.total_picks, 2, "Picks should be 2");
171
172 // 测试出队统计
173 scheduler.Dequeue(&idle_task);
174 stats = scheduler.GetStats();
175 EXPECT_EQ(stats.total_dequeues, 1, "Dequeues should be 1");
176
177 // 测试抢占统计
178 scheduler.OnPreempted(&idle_task);
179 stats = scheduler.GetStats();
180 EXPECT_EQ(stats.total_preemptions, 1, "Preemptions should be 1");
181
182 // 重置统计
183 scheduler.ResetStats();
184 stats = scheduler.GetStats();
185 EXPECT_EQ(stats.total_enqueues, 0, "Enqueues should be 0 after reset");
186 EXPECT_EQ(stats.total_dequeues, 0, "Dequeues should be 0 after reset");
187 EXPECT_EQ(stats.total_picks, 0, "Picks should be 0 after reset");
188 EXPECT_EQ(stats.total_preemptions, 0, "Preemptions should be 0 after reset");
189
190 klog::Info("test_idle_statistics passed");
191 return true;
192}
193
194auto test_idle_dequeue_wrong_task() -> bool {
195 klog::Info("Running test_idle_dequeue_wrong_task...");
196
197 IdleScheduler scheduler;
198
199 TaskControlBlock idle_task("IdleTask", 0, nullptr, nullptr);
200 TaskControlBlock other_task("OtherTask", 1, nullptr, nullptr);
201
202 scheduler.Enqueue(&idle_task);
203
204 // 移除不同的任务不应影响 idle 任务
205 scheduler.Dequeue(&other_task);
206 EXPECT_EQ(scheduler.GetQueueSize(), 1,
207 "Queue size should still be 1 after dequeue of wrong task");
208 EXPECT_EQ(scheduler.PickNext(), &idle_task,
209 "Idle task should still be present");
210
211 klog::Info("test_idle_dequeue_wrong_task passed");
212 return true;
213}
214
215auto test_idle_robustness() -> bool {
216 klog::Info("Running test_idle_robustness...");
217
218 IdleScheduler scheduler;
219
220 TaskControlBlock idle_task("IdleTask", 0, nullptr, nullptr);
221
222 // 空队列操作
223 EXPECT_EQ(scheduler.PickNext(), nullptr,
224 "PickNext on empty queue should return nullptr");
225 scheduler.Dequeue(&idle_task); // 不应崩溃
226
227 // 重复移除
228 scheduler.Enqueue(&idle_task);
229 scheduler.Dequeue(&idle_task);
230 scheduler.Dequeue(&idle_task); // 再次移除已移除的任务,不应崩溃
231 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty");
232
233 // 替换 idle 任务
234 TaskControlBlock idle_task2("IdleTask2", 0, nullptr, nullptr);
235 scheduler.Enqueue(&idle_task);
236 scheduler.Enqueue(&idle_task2); // 应替换为新的 idle 任务
237 EXPECT_EQ(scheduler.PickNext(), &idle_task2,
238 "New idle task should replace old one");
239
240 klog::Info("test_idle_robustness passed");
241 return true;
242}
243
244} // namespace
245
246auto idle_scheduler_test() -> bool {
247 klog::Info("\n=== Idle Scheduler System Tests ===\n");
248
249 if (!test_idle_basic_functionality()) {
250 return false;
251 }
252
253 if (!test_idle_pick_next_does_not_remove()) {
254 return false;
255 }
256
257 if (!test_idle_enqueue_dequeue()) {
258 return false;
259 }
260
261 if (!test_idle_on_tick_always_false()) {
262 return false;
263 }
264
265 if (!test_idle_on_time_slice_expired_always_false()) {
266 return false;
267 }
268
269 if (!test_idle_statistics()) {
270 return false;
271 }
272
273 if (!test_idle_dequeue_wrong_task()) {
274 return false;
275 }
276
277 if (!test_idle_robustness()) {
278 return false;
279 }
280
281 klog::Info("=== All Idle Scheduler Tests Passed ===\n");
282 return true;
283}
Idle 调度器
auto Dequeue(TaskControlBlock *task) -> void override
从队列中移除任务
auto Enqueue(TaskControlBlock *task) -> void override
将 idle 任务加入队列
auto GetQueueSize() const -> size_t override
获取队列大小
auto OnTimeSliceExpired(TaskControlBlock *task) -> bool override
时间片耗尽处理(idle 任务不使用时间片)
auto OnTick(TaskControlBlock *current) -> bool override
Tick 更新(idle 任务不需要时间片管理)
auto IsEmpty() const -> bool override
判断队列是否为空
auto PickNext() -> TaskControlBlock *override
选择下一个要运行的任务(返回 idle 任务)
auto OnPreempted(TaskControlBlock *task) -> void override
任务被抢占时的处理(idle 任务不需要特殊处理)
virtual auto GetStats() const -> const Stats &
获取调度器统计信息
const char * name
调度器名称
virtual auto ResetStats() -> void
重置统计信息
auto Receive(const etl::imessage &msg) -> void
向 FSM 发送消息
Definition task_fsm.hpp:163
auto idle_scheduler_test() -> bool
auto Info(etl::format_string< Args... > fmt, Args &&... args) -> void
以 INFO 级别记录日志
TaskControlBlock * idle_task
空闲任务
Definition per_cpu.hpp:6
调度消息(无负载,用作事件)
任务控制块,管理进程/线程的核心数据结构
TaskFsm fsm
任务状态机
#define EXPECT_TRUE(cond, msg)
#define EXPECT_FALSE(cond, msg)
#define EXPECT_EQ(val1, val2, msg)