mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-05-19 06:11:22 -04:00
Merge pull request #223 from arihant2math/unit-testing
This commit is contained in:
32
.github/workflows/test.yml
vendored
32
.github/workflows/test.yml
vendored
@@ -23,20 +23,34 @@ jobs:
|
||||
# If all features are disabled
|
||||
- name: Run tests inside docker container
|
||||
if: ${{ matrix.smp-feature == '' }}
|
||||
run: docker run moss "/bin/bash" -c "cargo run -r --no-default-features -- /bin/usertest" >> out.log
|
||||
run: |
|
||||
docker run moss "/bin/bash" -c "cargo run -r --no-default-features -- /bin/usertest" >> usertest.log
|
||||
docker run moss "/bin/bash" -c "cargo test -r --no-default-features" >> unittest.log
|
||||
# If any feature is enabled
|
||||
- name: Run tests inside docker container
|
||||
if: ${{ matrix.smp-feature == 'smp' }}
|
||||
run: docker run moss "/bin/bash" -c "cargo run -r --no-default-features --features "${{ matrix.smp-feature }}" -- /bin/usertest" >> out.log
|
||||
- name: Display test output
|
||||
run: cat out.log
|
||||
- name: Check for success line
|
||||
run: grep -q "All tests passed in " out.log || (echo "Tests failed" && exit 1)
|
||||
- name: Upload test output as artifact
|
||||
run: |
|
||||
docker run moss "/bin/bash" -c "cargo run -r --no-default-features --features "${{ matrix.smp-feature }}" -- /bin/usertest" >> usertest.log
|
||||
docker run moss "/bin/bash" -c "cargo test -r --no-default-features --features "${{ matrix.smp-feature }}"" >> unittest.log
|
||||
- name: Display usertest output
|
||||
run: cat usertest.log
|
||||
- name: Display unit test output
|
||||
run: cat unittest.log
|
||||
- name: Check for usertest success line
|
||||
run: grep -q "All tests passed in " usertest.log || (echo "Usertests failed" && exit 1)
|
||||
- name: Check for unit test success line
|
||||
run: |
|
||||
grep -q "test result: .*ok.*\..*passed" unittest.log || (echo "Unit tests failed" && exit 1)
|
||||
- name: Upload usertest output as artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: test-output-${{ matrix.smp-feature || 'up' }}
|
||||
path: out.log
|
||||
name: usertest-output-${{ matrix.smp-feature || 'up' }}
|
||||
path: usertest.log
|
||||
- name: Upload unittest output as artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: unittest-output-${{ matrix.smp-feature || 'up' }}
|
||||
path: unittest.log
|
||||
upload-image:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ name = "moss"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "moss"
|
||||
test = true
|
||||
bench = false
|
||||
|
||||
[dependencies]
|
||||
libkernel = { path = "libkernel" }
|
||||
aarch64-cpu = "11.1.0"
|
||||
|
||||
@@ -27,3 +27,20 @@ pub fn set_date(duration: Duration) {
|
||||
|
||||
// Represents a known duration since the epoch at the assoicated instant.
|
||||
static EPOCH_DURATION: SpinLock<Option<(Duration, Instant)>> = SpinLock::new(None);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ktest;
|
||||
|
||||
ktest! {
|
||||
fn test_date_and_set_date() {
|
||||
let initial_date = date();
|
||||
let new_date = Duration::from_secs(1_000_000);
|
||||
set_date(new_date);
|
||||
let updated_date = date();
|
||||
assert_ne!(initial_date, updated_date, "Date should change after set_date");
|
||||
assert!(updated_date >= new_date, "Updated date should be at least the new date set");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,3 +626,15 @@ impl VFS {
|
||||
fs.sync().await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::fs::VFS;
|
||||
use crate::ktest;
|
||||
|
||||
ktest! {
|
||||
async fn test_sync_all() {
|
||||
VFS.sync_all().await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,3 +101,24 @@ impl KPipe {
|
||||
self.inner.capacity()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::kernel::kpipe::KPipe;
|
||||
use crate::ktest;
|
||||
|
||||
ktest! {
|
||||
async fn kpipe_basic() {
|
||||
let pipe = KPipe::new().unwrap();
|
||||
pipe.push(1).await;
|
||||
pipe.push(2).await;
|
||||
pipe.push(3).await;
|
||||
let val1 = pipe.pop().await;
|
||||
let val2 = pipe.pop().await;
|
||||
let val3 = pipe.pop().await;
|
||||
assert_eq!(val1, 1);
|
||||
assert_eq!(val2, 2);
|
||||
assert_eq!(val3, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
src/main.rs
10
src/main.rs
@@ -3,6 +3,11 @@
|
||||
#![feature(used_with_arg)]
|
||||
#![feature(likely_unlikely)]
|
||||
#![feature(box_as_ptr)]
|
||||
#![expect(internal_features)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(custom_test_frameworks)]
|
||||
#![reexport_test_harness_main = "test_main"]
|
||||
#![test_runner(crate::testing::test_runner)]
|
||||
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
@@ -45,6 +50,8 @@ mod memory;
|
||||
mod process;
|
||||
mod sched;
|
||||
mod sync;
|
||||
#[cfg(test)]
|
||||
pub mod testing;
|
||||
|
||||
#[panic_handler]
|
||||
fn on_panic(info: &PanicInfo) -> ! {
|
||||
@@ -161,6 +168,9 @@ async fn launch_init(mut opts: KOptions) {
|
||||
.expect("Could not clone FD");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
test_main();
|
||||
|
||||
drop(task);
|
||||
|
||||
let mut init_args = vec![init.as_str().to_string()];
|
||||
|
||||
122
src/testing/mod.rs
Normal file
122
src/testing/mod.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use crate::arch::{Arch, ArchImpl};
|
||||
use crate::console::write_fmt;
|
||||
use crate::drivers::timer::uptime;
|
||||
use alloc::format;
|
||||
use core::fmt::Display;
|
||||
|
||||
const TEXT_GREEN: &str = "\x1b[32m";
|
||||
const TEXT_RED: &str = "\x1b[31m";
|
||||
const TEXT_RESET: &str = "\x1b[0m";
|
||||
|
||||
pub enum TestResult {
|
||||
Ok,
|
||||
Failed,
|
||||
Skipped,
|
||||
}
|
||||
|
||||
impl Display for TestResult {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
TestResult::Ok => write!(f, "{TEXT_GREEN}ok{TEXT_RESET}"),
|
||||
TestResult::Failed => write!(f, "{TEXT_GREEN}failed{TEXT_RESET}"),
|
||||
TestResult::Skipped => write!(f, "skipped"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Test {
|
||||
pub name: &'static str,
|
||||
pub test_fn: fn() -> TestResult,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_runner(tests: &[&Test]) {
|
||||
write_fmt(format_args!("\nrunning {} tests\n", tests.len())).unwrap();
|
||||
let mut passed = 0;
|
||||
let mut failed = 0;
|
||||
let mut ignored = 0;
|
||||
let start = uptime();
|
||||
for test in tests {
|
||||
let result = (test.test_fn)();
|
||||
match result {
|
||||
TestResult::Ok => passed += 1,
|
||||
TestResult::Failed => failed += 1,
|
||||
TestResult::Skipped => ignored += 1,
|
||||
}
|
||||
write_fmt(format_args!("test {} ... {}\n", test.name, result)).unwrap();
|
||||
}
|
||||
let duration = uptime() - start;
|
||||
write_fmt(format_args!(
|
||||
"\ntest result: {}. {passed} passed; {failed} failed; {ignored} ignored; finished in {}.{}s\n",
|
||||
if failed == 0 {
|
||||
format!("{TEXT_GREEN}ok{TEXT_RESET}")
|
||||
} else {
|
||||
format!("{TEXT_RED}FAILED{TEXT_RESET}")
|
||||
},
|
||||
duration.as_secs(),
|
||||
duration.subsec_millis() / 10
|
||||
))
|
||||
.unwrap();
|
||||
ArchImpl::power_off();
|
||||
}
|
||||
|
||||
pub fn panic_noop(_: *mut u8, _: *mut u8) {}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ktest {
|
||||
($name:ident, fn $fn_name:ident() $body:block) => {
|
||||
#[cfg(test)]
|
||||
fn $fn_name(_: *mut u8) {
|
||||
$body
|
||||
}
|
||||
|
||||
paste::paste! {
|
||||
#[cfg(test)]
|
||||
#[test_case]
|
||||
static [<__TEST_ $name>]: crate::testing::Test = crate::testing::Test {
|
||||
name: concat!(module_path!(), "::", stringify!($name)),
|
||||
test_fn: || {
|
||||
let result = unsafe {
|
||||
core::intrinsics::catch_unwind(
|
||||
$fn_name as fn(*mut u8),
|
||||
core::ptr::null_mut(),
|
||||
crate::testing::panic_noop,
|
||||
)
|
||||
};
|
||||
match result {
|
||||
0 => crate::testing::TestResult::Ok,
|
||||
1 => crate::testing::TestResult::Failed,
|
||||
_ => unreachable!("catch_unwind should only return 0 or 1"),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
(fn $name:ident() $body:block) => {
|
||||
crate::ktest!($name, fn $name() $body);
|
||||
};
|
||||
(async fn $name:ident() $body:block) => {
|
||||
async fn $name() {
|
||||
$body
|
||||
}
|
||||
|
||||
paste::paste! {
|
||||
crate::ktest! {
|
||||
$name,
|
||||
fn [<__sync_ $name>]() {
|
||||
let mut fut = alloc::boxed::Box::pin($name());
|
||||
let desc = crate::process::TaskDescriptor::from_tgid_tid(crate::process::thread_group::Tgid(0), crate::process::Tid(0));
|
||||
let waker = crate::sched::waker::create_waker(desc);
|
||||
let mut ctx = core::task::Context::from_waker(&waker);
|
||||
loop {
|
||||
match fut.as_mut().poll(&mut ctx) {
|
||||
core::task::Poll::Ready(()) => break,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user