Skip to content

Commit 69a10d4

Browse files
committed
test: fix st
Signed-off-by: Niu Zhihong <zhihong@nzhnb.com>
1 parent dd2a75d commit 69a10d4

3 files changed

Lines changed: 216 additions & 16 deletions

File tree

CMakePresets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
},
9898
"SIMPLEKERNEL_DEFAULT_STACK_SIZE": {
9999
"type": "STRING",
100-
"value": "4096"
100+
"value": "16384"
101101
},
102102
"SIMPLEKERNEL_TICK": {
103103
"type": "STRING",

tests/system_test/main.cpp

Lines changed: 194 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* @copyright Copyright The SimpleKernel Contributors
33
*/
44

5+
#include <atomic>
56
#include <cstdint>
67
#include <new>
78

@@ -11,10 +12,12 @@
1112
#include "kernel_log.hpp"
1213
#include "kstd_cstdio"
1314
#include "kstd_libcxx.h"
15+
#include "kstd_memory"
1416
#include "per_cpu.hpp"
1517
#include "spinlock.hpp"
1618
#include "syscall.hpp"
1719
#include "system_test.h"
20+
#include "task_control_block.hpp"
1821
#include "task_manager.hpp"
1922

2023
namespace {
@@ -25,7 +28,9 @@ struct test_case {
2528
bool is_smp_test = false;
2629
};
2730

28-
std::array<test_case, 18> test_cases = {
31+
constexpr size_t kTestCount = 18;
32+
33+
std::array<test_case, kTestCount> test_cases = {
2934
test_case{"ctor_dtor_test", ctor_dtor_test, false},
3035
test_case{"spinlock_test", spinlock_test, true},
3136
test_case{"memory_test", memory_test, false},
@@ -45,27 +50,160 @@ std::array<test_case, 18> test_cases = {
4550
test_case{"kernel_task_test", kernel_task_test, false},
4651
test_case{"user_task_test", user_task_test, false}};
4752

48-
void run_tests_main() {
53+
std::array<TestResult, kTestCount> test_results{};
54+
55+
std::atomic<size_t> g_cores_ready{0};
56+
57+
void test_thread_entry(void* arg) {
58+
auto idx = reinterpret_cast<uint64_t>(arg);
59+
auto& test = test_cases[idx];
60+
61+
klog::Info("[TEST] Starting: {}", test.name);
62+
63+
bool passed = test.func();
64+
65+
klog::Info("[TEST] Finished: {} — {}", test.name, passed ? "PASS" : "FAIL");
66+
67+
sys_exit(passed ? 0 : 1);
68+
}
69+
70+
void print_test_summary() {
4971
int passed = 0;
5072
int failed = 0;
73+
int timed_out = 0;
5174

52-
for (const auto& test : test_cases) {
53-
klog::Info("----{}----", test.name);
54-
if (test.func()) {
55-
passed++;
56-
klog::Info("----{} PASS----", test.name);
75+
klog::Info("========================================");
76+
klog::Info(" System Test Results");
77+
klog::Info("========================================");
78+
79+
for (size_t i = 0; i < kTestCount; ++i) {
80+
auto& r = test_results[i];
81+
const char* tag = "???";
82+
83+
switch (r.status) {
84+
case TestThreadStatus::kPassed:
85+
tag = "PASS";
86+
passed++;
87+
break;
88+
case TestThreadStatus::kFailed:
89+
tag = "FAIL";
90+
failed++;
91+
break;
92+
case TestThreadStatus::kTimeout:
93+
tag = "TIMEOUT";
94+
timed_out++;
95+
break;
96+
case TestThreadStatus::kRunning:
97+
tag = "TIMEOUT";
98+
timed_out++;
99+
break;
100+
default:
101+
tag = "SKIP";
102+
break;
103+
}
104+
105+
if (test_cases[i].is_smp_test) {
106+
klog::Info(" [{}] {} (SMP, exit_code={})", tag, r.name, r.exit_code);
57107
} else {
58-
failed++;
59-
klog::Err("----{} FAIL----", test.name);
108+
klog::Info(" [{}] {} (pid={}, exit_code={})", tag, r.name, r.pid,
109+
r.exit_code);
60110
}
61111
}
62112

113+
int total = static_cast<int>(kTestCount);
114+
63115
klog::Info("========================================");
64-
klog::Info("Result: {} passed, {} failed, {} total", passed, failed,
65-
passed + failed);
116+
klog::Info(" Total: {} | Passed: {} | Failed: {} | Timeout: {}", total,
117+
passed, failed, timed_out);
66118
klog::Info("========================================");
119+
}
120+
121+
void test_runner_entry(void* /*arg*/) {
122+
auto& task_mgr = TaskManagerSingleton::instance();
123+
auto* runner = task_mgr.GetCurrentTask();
124+
125+
// Phase 2: 为每个非 SMP 测试创建独立线程,建立父子关系
126+
int thread_test_count = 0;
127+
128+
for (size_t i = 0; i < kTestCount; ++i) {
129+
if (test_cases[i].is_smp_test) {
130+
continue;
131+
}
132+
133+
auto task = kstd::make_unique<TaskControlBlock>(
134+
test_cases[i].name, 10, test_thread_entry, reinterpret_cast<void*>(i));
135+
136+
// Wait() requires parent-child relationship to collect exit status
137+
task->aux->parent_pid = runner->pid;
138+
task->aux->pgid = runner->aux->pgid;
139+
140+
test_results[i].pid = task->pid;
141+
test_results[i].status = TestThreadStatus::kRunning;
142+
143+
task_mgr.AddTask(std::move(task));
144+
thread_test_count++;
145+
}
146+
147+
klog::Info("[RUNNER] Spawned {} test threads, collecting via Wait()...",
148+
thread_test_count);
149+
150+
// Phase 3: 通过 Wait() 收集所有测试线程的退出状态
151+
int collected = 0;
152+
constexpr int kMaxWaitRetries = 1200; // 1200 * 50ms = 60s 超时
153+
int retries = 0;
154+
155+
while (collected < thread_test_count && retries < kMaxWaitRetries) {
156+
int status = 0;
157+
auto wait_result =
158+
task_mgr.Wait(static_cast<Pid>(-1), &status, true, false);
67159

68-
QemuExit(failed == 0);
160+
if (wait_result.has_value() && wait_result.value() > 0) {
161+
Pid exited_pid = wait_result.value();
162+
163+
for (size_t i = 0; i < kTestCount; ++i) {
164+
if (test_results[i].pid == static_cast<int64_t>(exited_pid)) {
165+
test_results[i].exit_code = status;
166+
test_results[i].status = (status == 0) ? TestThreadStatus::kPassed
167+
: TestThreadStatus::kFailed;
168+
169+
klog::Info("[RUNNER] Collected: {} (pid={}, exit_code={}) — {}",
170+
test_cases[i].name, exited_pid, status,
171+
(status == 0) ? "PASS" : "FAIL");
172+
break;
173+
}
174+
}
175+
176+
collected++;
177+
} else {
178+
(void)sys_sleep(50);
179+
retries++;
180+
}
181+
}
182+
183+
for (size_t i = 0; i < kTestCount; ++i) {
184+
if (test_cases[i].is_smp_test) {
185+
continue;
186+
}
187+
if (test_results[i].status == TestThreadStatus::kPending ||
188+
test_results[i].status == TestThreadStatus::kRunning) {
189+
test_results[i].status = TestThreadStatus::kTimeout;
190+
klog::Err("[RUNNER] Timeout: {} (pid={})", test_cases[i].name,
191+
test_results[i].pid);
192+
}
193+
}
194+
195+
// Phase 4: 汇总打印
196+
print_test_summary();
197+
198+
bool all_passed = true;
199+
for (size_t i = 0; i < kTestCount; ++i) {
200+
if (test_results[i].status != TestThreadStatus::kPassed) {
201+
all_passed = false;
202+
break;
203+
}
204+
}
205+
206+
QemuExit(all_passed);
69207
}
70208

71209
void run_tests_smp() {
@@ -85,18 +223,21 @@ auto main_smp(int argc, const char** argv) -> int {
85223
TimerInitSMP();
86224
klog::Info("Hello SimpleKernel SMP");
87225

226+
g_cores_ready.fetch_add(1, std::memory_order_release);
227+
88228
run_tests_smp();
89229

230+
TaskManagerSingleton::instance().Schedule();
231+
90232
__builtin_unreachable();
91233
}
92234

93235
} // namespace
94236

95-
void _start(int argc, const char** argv) {
237+
auto _start(int argc, const char** argv) -> void {
96238
if (argv != nullptr) {
97239
CppInit();
98240
main(argc, argv);
99-
CppDeInit();
100241
} else {
101242
main_smp(argc, argv);
102243
}
@@ -127,7 +268,45 @@ auto main(int argc, const char** argv) -> int {
127268

128269
klog::Info("Hello SimpleKernel");
129270

130-
run_tests_main();
271+
for (size_t i = 0; i < kTestCount; ++i) {
272+
test_results[i].name = test_cases[i].name;
273+
test_results[i].status = TestThreadStatus::kPending;
274+
test_results[i].exit_code = -1;
275+
test_results[i].pid = 0;
276+
}
277+
278+
// Phase 1: SMP 测试同步运行(需要跨核屏障协调,必须在调度器启动前完成)
279+
size_t expected_cores = BasicInfoSingleton::instance().core_count - 1;
280+
klog::Info("[RUNNER] Waiting for {} secondary core(s) to initialize...",
281+
expected_cores);
282+
while (g_cores_ready.load(std::memory_order_acquire) < expected_cores) {
283+
cpu_io::Pause();
284+
}
285+
klog::Info("[RUNNER] All cores ready, starting SMP tests");
286+
287+
for (size_t i = 0; i < kTestCount; ++i) {
288+
if (!test_cases[i].is_smp_test) {
289+
continue;
290+
}
291+
auto& result = test_results[i];
292+
result.status = TestThreadStatus::kRunning;
293+
294+
klog::Info("[SMP] Running: {}", test_cases[i].name);
295+
bool smp_passed = test_cases[i].func();
296+
297+
result.exit_code = smp_passed ? 0 : 1;
298+
result.status =
299+
smp_passed ? TestThreadStatus::kPassed : TestThreadStatus::kFailed;
300+
301+
klog::Info("[SMP] Finished: {} — {}", test_cases[i].name,
302+
smp_passed ? "PASS" : "FAIL");
303+
}
304+
305+
auto runner = kstd::make_unique<TaskControlBlock>("test_runner", 10,
306+
test_runner_entry, nullptr);
307+
TaskManagerSingleton::instance().AddTask(std::move(runner));
308+
309+
TaskManagerSingleton::instance().Schedule();
131310

132311
__builtin_unreachable();
133312
}

tests/system_test/system_test.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,24 @@ inline void QemuExit([[maybe_unused]] bool success) {
213213
#endif
214214
__builtin_unreachable();
215215
}
216+
217+
// ===========================================================================
218+
// Test thread result tracking
219+
// ===========================================================================
220+
221+
/// 测试线程运行状态
222+
enum class TestThreadStatus : uint8_t {
223+
kPending, ///< 等待启动
224+
kRunning, ///< 运行中
225+
kPassed, ///< 测试通过
226+
kFailed, ///< 测试失败
227+
kTimeout, ///< 超时未完成
228+
};
229+
230+
/// 单个测试线程的结果记录
231+
struct TestResult {
232+
const char* name = nullptr;
233+
int64_t pid = 0;
234+
TestThreadStatus status = TestThreadStatus::kPending;
235+
int exit_code = -1;
236+
};

0 commit comments

Comments
 (0)