Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bring back test harness #293

Merged
merged 4 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 37 additions & 19 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 @@ -104,6 +104,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 @@ -144,6 +145,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
84 changes: 46 additions & 38 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
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