Skip to content

Commit

Permalink
feat: bring back test harness
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasKruckenberg committed Feb 15, 2025
1 parent 2563aed commit 6b1e44e
Show file tree
Hide file tree
Showing 11 changed files with 654 additions and 40 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = ["kernel", "libs/*", "loader", "loader/api"]
members = ["kernel", "libs/*", "loader", "loader/api", "libs/ktest/macros"]
resolver = "2"

[workspace.package]
Expand Down Expand Up @@ -101,6 +101,7 @@ wavltree = { path = "libs/wavltree" }
loader-api = { path = "loader/api" }
fdt = { path = "libs/fdt" }
ksharded-slab = { path = "libs/ksharded-slab" }
ktest = { path = "libs/ktest" }

# third-party dependencies
cfg-if = "1.0.0"
Expand Down Expand Up @@ -141,6 +142,11 @@ cranelift-frontend = { git = "https://github.com/JonasKruckenberg/wasmtime.git",
cranelift-entity = { git = "https://github.com/JonasKruckenberg/wasmtime.git", branch = "main", default-features = false }
wasmtime-slab = "28.0.1"

# build dependencies
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["full"] }

[profile.release]
debug = "limited" # The kernel should be able to print stack traces of itself even in release mode

Expand Down
1 change: 1 addition & 0 deletions kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ wavltree = { workspace = true, features = ["dot"] }
mpsc-queue.workspace = true
fdt.workspace = true
ksharded-slab.workspace = true
ktest.workspace = true

log.workspace = true
cfg-if.workspace = true
Expand Down
86 changes: 47 additions & 39 deletions kernel/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ mod metrics;
mod panic;
mod scheduler;
mod task;
#[cfg(test)]
mod tests;
mod time;
mod tracing;
mod traps;
Expand All @@ -50,10 +52,10 @@ use crate::time::clock::Ticks;
use crate::time::Instant;
use crate::vm::bootstrap_alloc::BootstrapAllocator;
use arrayvec::ArrayVec;
use cfg_if::cfg_if;
use core::cell::Cell;
use core::range::Range;
use core::slice;
use core::time::Duration;
use cpu_local::cpu_local;
use loader_api::{BootInfo, LoaderConfig, MemoryRegionKind};
use rand::SeedableRng;
Expand Down Expand Up @@ -144,52 +146,58 @@ fn _start(cpuid: usize, boot_info: &'static BootInfo, boot_ticks: u64) -> ! {
tracing::per_cpu_init_late(Instant::from_ticks(Ticks(boot_ticks)));

// initialize the executor
let sched = scheduler::init(boot_info.cpu_mask.count_ones() as usize);
let _sched = scheduler::init(boot_info.cpu_mask.count_ones() as usize);

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

if cpuid == 0 {
sched.spawn(async move {
tracing::debug!("before timeout");
let start = Instant::now();
let res =
time::timeout(Duration::from_secs(1), time::sleep(Duration::from_secs(5))).await;
tracing::debug!("after timeout {res:?}");
assert!(res.is_err());
assert_eq!(start.elapsed().as_secs(), 1);

tracing::debug!("before timeout");
let start = Instant::now();
let res =
time::timeout(Duration::from_secs(5), time::sleep(Duration::from_secs(1))).await;
tracing::debug!("after timeout {res:?}");
assert!(res.is_ok());
assert_eq!(start.elapsed().as_secs(), 1);

tracing::debug!("sleeping for 1 sec...");
let start = Instant::now();
time::sleep(Duration::from_secs(1)).await;
assert_eq!(start.elapsed().as_secs(), 1);
tracing::debug!("slept 1 sec! {:?}", start.elapsed());

// FIXME this is a quite terrible hack to get the scheduler to close in tests (otherwise
// tests would run forever) we should find a proper way to shut down the scheduler when idle.
#[cfg(test)]
scheduler::scheduler().shutdown();
});

// scheduler::scheduler().spawn(async move {
// tracing::debug!("Point A");
// scheduler::yield_now().await;
// tracing::debug!("Point B");
// });
cfg_if! {
if #[cfg(test)] {
let mut output = riscv::hio::HostStream::new_stderr();
tests::run_tests(&mut output, boot_info);
} else {
scheduler::Worker::new(_sched, cpuid, &mut rng).run();
}
}

scheduler::Worker::new(sched, cpuid, &mut rng).run();
// if cpuid == 0 {
// sched.spawn(async move {
// tracing::debug!("before timeout");
// let start = Instant::now();
// let res =
// time::timeout(Duration::from_secs(1), time::sleep(Duration::from_secs(5))).await;
// tracing::debug!("after timeout {res:?}");
// assert!(res.is_err());
// assert_eq!(start.elapsed().as_secs(), 1);
//
// tracing::debug!("before timeout");
// let start = Instant::now();
// let res =
// time::timeout(Duration::from_secs(5), time::sleep(Duration::from_secs(1))).await;
// tracing::debug!("after timeout {res:?}");
// assert!(res.is_ok());
// assert_eq!(start.elapsed().as_secs(), 1);
//
// tracing::debug!("sleeping for 1 sec...");
// let start = Instant::now();
// time::sleep(Duration::from_secs(1)).await;
// assert_eq!(start.elapsed().as_secs(), 1);
// tracing::debug!("slept 1 sec! {:?}", start.elapsed());
//
//
// #[cfg(test)]
// scheduler::scheduler().shutdown();
// });
//
// // scheduler::scheduler().spawn(async move {
// // tracing::debug!("Point A");
// // scheduler::yield_now().await;
// // tracing::debug!("Point B");
// // });
// }

// wasm::test();

Expand Down Expand Up @@ -272,4 +280,4 @@ fn locate_device_tree(boot_info: &BootInfo) -> (&'static [u8], Range<PhysicalAdd
slice,
Range::from(PhysicalAddress::new(fdt.range.start)..PhysicalAddress::new(fdt.range.end)),
)
}
}
60 changes: 60 additions & 0 deletions kernel/src/tests/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2025 Jonas Kruckenberg
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use ktest::Test;

#[derive(Default)]
pub struct Arguments<'a> {
pub test_name: Option<&'a str>,
pub list: bool,
pub include_ignored: bool,
pub ignored: bool,
pub exact: bool,
pub format: FormatSetting,
}

#[derive(Default, Copy, Clone)]
pub enum FormatSetting {
#[default]
Pretty,
Terse,
Json,
}

impl<'a> Arguments<'a> {
#[allow(clippy::should_implement_trait)]
pub fn from_str(str: &'a str) -> Self {
Self::parse(str.split_ascii_whitespace())
}

pub fn parse(mut iter: impl Iterator<Item = &'a str>) -> Self {
let mut out = Self::default();

while let Some(str) = iter.next() {
match str {
"--list" => out.list = true,
"--include-ignored" => out.include_ignored = true,
"--ignored" => out.ignored = true,
"--exact" => out.exact = true,
"--format" => match iter.next().unwrap() {
"pretty" => out.format = FormatSetting::Pretty,
"terse" => out.format = FormatSetting::Terse,
"json" => out.format = FormatSetting::Json,
_ => {}
},
_ => out.test_name = Some(str),
}
}

out
}

/// Returns `true` if the given test should be ignored.
pub fn is_ignored(&self, test: &Test) -> bool {
test.info.ignored && !self.ignored && !self.include_ignored
}
}
Loading

0 comments on commit 6b1e44e

Please sign in to comment.