Skip to content

Commit f28e91d

Browse files
feat: bring back test harness (#293)
* feat: bring back test harness * Update main.rs * Create Cargo.lock
1 parent ef15c2d commit f28e91d

File tree

11 files changed

+672
-58
lines changed

11 files changed

+672
-58
lines changed

Cargo.lock

+37-19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = ["kernel", "libs/*", "loader", "loader/api"]
2+
members = ["kernel", "libs/*", "loader", "loader/api", "libs/ktest/macros"]
33
resolver = "2"
44

55
[workspace.package]
@@ -104,6 +104,7 @@ wavltree = { path = "libs/wavltree" }
104104
loader-api = { path = "loader/api" }
105105
fdt = { path = "libs/fdt" }
106106
ksharded-slab = { path = "libs/ksharded-slab" }
107+
ktest = { path = "libs/ktest" }
107108

108109
# third-party dependencies
109110
cfg-if = "1.0.0"
@@ -144,6 +145,11 @@ cranelift-frontend = { git = "https://github.com/JonasKruckenberg/wasmtime.git",
144145
cranelift-entity = { git = "https://github.com/JonasKruckenberg/wasmtime.git", branch = "main", default-features = false }
145146
wasmtime-slab = "28.0.1"
146147

148+
# build dependencies
149+
proc-macro2 = "1"
150+
quote = "1"
151+
syn = { version = "2", features = ["full"] }
152+
147153
[profile.release]
148154
debug = "limited" # The kernel should be able to print stack traces of itself even in release mode
149155

kernel/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ wavltree = { workspace = true, features = ["dot"] }
2323
mpsc-queue.workspace = true
2424
fdt.workspace = true
2525
ksharded-slab.workspace = true
26+
ktest.workspace = true
2627

2728
log.workspace = true
2829
cfg-if.workspace = true

kernel/src/main.rs

+46-38
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ mod metrics;
3737
mod panic;
3838
mod scheduler;
3939
mod task;
40+
#[cfg(test)]
41+
mod tests;
4042
mod time;
4143
mod tracing;
4244
mod traps;
@@ -50,10 +52,10 @@ use crate::time::clock::Ticks;
5052
use crate::time::Instant;
5153
use crate::vm::bootstrap_alloc::BootstrapAllocator;
5254
use arrayvec::ArrayVec;
55+
use cfg_if::cfg_if;
5356
use core::cell::Cell;
5457
use core::range::Range;
5558
use core::slice;
56-
use core::time::Duration;
5759
use cpu_local::cpu_local;
5860
use loader_api::{BootInfo, LoaderConfig, MemoryRegionKind};
5961
use rand::SeedableRng;
@@ -144,52 +146,58 @@ fn _start(cpuid: usize, boot_info: &'static BootInfo, boot_ticks: u64) -> ! {
144146
tracing::per_cpu_init_late(Instant::from_ticks(Ticks(boot_ticks)));
145147

146148
// initialize the executor
147-
let sched = scheduler::init(boot_info.cpu_mask.count_ones() as usize);
149+
let _sched = scheduler::init(boot_info.cpu_mask.count_ones() as usize);
148150

149151
tracing::info!(
150152
"Booted in ~{:?} ({:?} in k23)",
151153
Instant::now().duration_since(Instant::ZERO),
152154
Instant::from_ticks(Ticks(boot_ticks)).elapsed()
153155
);
154156

155-
if cpuid == 0 {
156-
sched.spawn(async move {
157-
tracing::debug!("before timeout");
158-
let start = Instant::now();
159-
let res =
160-
time::timeout(Duration::from_secs(1), time::sleep(Duration::from_secs(5))).await;
161-
tracing::debug!("after timeout {res:?}");
162-
assert!(res.is_err());
163-
assert_eq!(start.elapsed().as_secs(), 1);
164-
165-
tracing::debug!("before timeout");
166-
let start = Instant::now();
167-
let res =
168-
time::timeout(Duration::from_secs(5), time::sleep(Duration::from_secs(1))).await;
169-
tracing::debug!("after timeout {res:?}");
170-
assert!(res.is_ok());
171-
assert_eq!(start.elapsed().as_secs(), 1);
172-
173-
tracing::debug!("sleeping for 1 sec...");
174-
let start = Instant::now();
175-
time::sleep(Duration::from_secs(1)).await;
176-
assert_eq!(start.elapsed().as_secs(), 1);
177-
tracing::debug!("slept 1 sec! {:?}", start.elapsed());
178-
179-
// FIXME this is a quite terrible hack to get the scheduler to close in tests (otherwise
180-
// tests would run forever) we should find a proper way to shut down the scheduler when idle.
181-
#[cfg(test)]
182-
scheduler::scheduler().shutdown();
183-
});
184-
185-
// scheduler::scheduler().spawn(async move {
186-
// tracing::debug!("Point A");
187-
// scheduler::yield_now().await;
188-
// tracing::debug!("Point B");
189-
// });
157+
cfg_if! {
158+
if #[cfg(test)] {
159+
let mut output = riscv::hio::HostStream::new_stderr();
160+
tests::run_tests(&mut output, boot_info);
161+
} else {
162+
scheduler::Worker::new(_sched, cpuid, &mut rng).run();
163+
}
190164
}
191165

192-
scheduler::Worker::new(sched, cpuid, &mut rng).run();
166+
// if cpuid == 0 {
167+
// sched.spawn(async move {
168+
// tracing::debug!("before timeout");
169+
// let start = Instant::now();
170+
// let res =
171+
// time::timeout(Duration::from_secs(1), time::sleep(Duration::from_secs(5))).await;
172+
// tracing::debug!("after timeout {res:?}");
173+
// assert!(res.is_err());
174+
// assert_eq!(start.elapsed().as_secs(), 1);
175+
//
176+
// tracing::debug!("before timeout");
177+
// let start = Instant::now();
178+
// let res =
179+
// time::timeout(Duration::from_secs(5), time::sleep(Duration::from_secs(1))).await;
180+
// tracing::debug!("after timeout {res:?}");
181+
// assert!(res.is_ok());
182+
// assert_eq!(start.elapsed().as_secs(), 1);
183+
//
184+
// tracing::debug!("sleeping for 1 sec...");
185+
// let start = Instant::now();
186+
// time::sleep(Duration::from_secs(1)).await;
187+
// assert_eq!(start.elapsed().as_secs(), 1);
188+
// tracing::debug!("slept 1 sec! {:?}", start.elapsed());
189+
//
190+
//
191+
// #[cfg(test)]
192+
// scheduler::scheduler().shutdown();
193+
// });
194+
//
195+
// // scheduler::scheduler().spawn(async move {
196+
// // tracing::debug!("Point A");
197+
// // scheduler::yield_now().await;
198+
// // tracing::debug!("Point B");
199+
// // });
200+
// }
193201

194202
// wasm::test();
195203

kernel/src/tests/args.rs

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2025 Jonas Kruckenberg
2+
//
3+
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4+
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5+
// http://opensource.org/licenses/MIT>, at your option. This file may not be
6+
// copied, modified, or distributed except according to those terms.
7+
8+
use ktest::Test;
9+
10+
#[derive(Default)]
11+
pub struct Arguments<'a> {
12+
pub test_name: Option<&'a str>,
13+
pub list: bool,
14+
pub include_ignored: bool,
15+
pub ignored: bool,
16+
pub exact: bool,
17+
pub format: FormatSetting,
18+
}
19+
20+
#[derive(Default, Copy, Clone)]
21+
pub enum FormatSetting {
22+
#[default]
23+
Pretty,
24+
Terse,
25+
Json,
26+
}
27+
28+
impl<'a> Arguments<'a> {
29+
#[allow(clippy::should_implement_trait)]
30+
pub fn from_str(str: &'a str) -> Self {
31+
Self::parse(str.split_ascii_whitespace())
32+
}
33+
34+
pub fn parse(mut iter: impl Iterator<Item = &'a str>) -> Self {
35+
let mut out = Self::default();
36+
37+
while let Some(str) = iter.next() {
38+
match str {
39+
"--list" => out.list = true,
40+
"--include-ignored" => out.include_ignored = true,
41+
"--ignored" => out.ignored = true,
42+
"--exact" => out.exact = true,
43+
"--format" => match iter.next().unwrap() {
44+
"pretty" => out.format = FormatSetting::Pretty,
45+
"terse" => out.format = FormatSetting::Terse,
46+
"json" => out.format = FormatSetting::Json,
47+
_ => {}
48+
},
49+
_ => out.test_name = Some(str),
50+
}
51+
}
52+
53+
out
54+
}
55+
56+
/// Returns `true` if the given test should be ignored.
57+
pub fn is_ignored(&self, test: &Test) -> bool {
58+
test.info.ignored && !self.ignored && !self.include_ignored
59+
}
60+
}

0 commit comments

Comments
 (0)