22 * @copyright Copyright The SimpleKernel Contributors
33 */
44
5+ #include < atomic>
56#include < cstdint>
67#include < new>
78
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
2023namespace {
@@ -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
71209void 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}
0 commit comments