Create syscall recorder and write first test
diff --git a/src/leds.rs b/src/leds.rs index e0f420c..c505cf9 100644 --- a/src/leds.rs +++ b/src/leds.rs
@@ -127,3 +127,35 @@ } } } + +#[cfg(test)] +mod test { + use super::command_nr; + use super::DRIVER_NUMBER; + use crate::result::TockResult; + use crate::syscalls; + use crate::syscalls::raw::Event; + + #[test] + pub fn single_led_can_be_enabled() { + let events = syscalls::raw::run_recording_events::<TockResult<()>, _>(|next_return| { + let mut drivers = unsafe { crate::drivers::retrieve_drivers_unsafe() }; + + next_return.set(1); + + let leds_driver = drivers.leds.init_driver()?; + next_return.set(0); + + let led = leds_driver.get(0)?; + led.on()?; + Ok(()) + }); + assert_eq!( + events, + vec![ + Event::Command(DRIVER_NUMBER, command_nr::COUNT, 0, 0), + Event::Command(DRIVER_NUMBER, command_nr::ON, 0, 0), + ] + ); + } +}
diff --git a/src/syscalls/platform_mock.rs b/src/syscalls/platform_mock.rs index 30c528f..66e79f0 100644 --- a/src/syscalls/platform_mock.rs +++ b/src/syscalls/platform_mock.rs
@@ -1,49 +1,103 @@ +use core::cell::Cell; +use core::cell::RefCell; +use std::vec::Vec; + /// yield for a callback fired by the kernel /// # Safety /// Yielding inside a callback conflicts with Rust's safety guarantees. For example, /// a FnMut closure could be triggered multiple times making a &mut a shared reference. -pub unsafe fn yieldk() {} +pub unsafe fn yieldk() { + EVENTS.with(|e| e.borrow_mut().push(Event::YieldK)); +} /// Subscribe a callback to the kernel /// # Safety /// Unsafe as passed callback is dereferenced and called. pub unsafe fn subscribe( - _: usize, - _: usize, - _: *const unsafe extern "C" fn(usize, usize, usize, usize), - _: usize, + arg1: usize, + arg2: usize, + arg3: *const unsafe extern "C" fn(usize, usize, usize, usize), + arg4: usize, ) -> isize { - unimplemented() + EVENTS.with(|e| { + e.borrow_mut() + .push(Event::Subscribe(arg1, arg2, arg3, arg4)) + }); + NEXT_OUTPUT.with(|e| e.get()) } /// Send a command to the tock kernel /// # Safety /// This function usually involves assembly calls which are unsafe. -pub unsafe fn command(_: usize, _: usize, _: usize, _: usize) -> isize { - unimplemented() +pub unsafe fn command(arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> isize { + EVENTS.with(|e| e.borrow_mut().push(Event::Command(arg1, arg2, arg3, arg4))); + NEXT_OUTPUT.with(|e| e.get()) } /// Call a command only taking into accoun the first argument /// # Safety /// Unsafe as ignored arguments cause leaking of registers to the kernel -pub unsafe fn command1(_: usize, _: usize, _: usize) -> isize { - unimplemented() +pub unsafe fn command1(arg1: usize, arg2: usize, arg3: usize) -> isize { + EVENTS.with(|e| e.borrow_mut().push(Event::Command1(arg1, arg2, arg3))); + NEXT_OUTPUT.with(|e| e.get()) } /// Share a memory region with the kernel /// # Safety /// Unsafe as the pointer to the shared buffer is potentially dereferenced by the kernel. -pub unsafe fn allow(_: usize, _: usize, _: *mut u8, _: usize) -> isize { - unimplemented() +pub unsafe fn allow(arg1: usize, arg2: usize, arg3: *mut u8, arg4: usize) -> isize { + EVENTS.with(|e| e.borrow_mut().push(Event::Allow(arg1, arg2, arg3, arg4))); + NEXT_OUTPUT.with(|e| e.get()) } /// Generic operations on the app's memory as requesting more memory /// # Safety /// Allows the kernel to do generic operations on the app's memory which can cause memory corruption. -pub unsafe fn memop(_: u32, _: usize) -> isize { - unimplemented() +pub unsafe fn memop(arg1: u32, arg2: usize) -> isize { + EVENTS.with(|e| e.borrow_mut().push(Event::Memop(arg1, arg2))); + NEXT_OUTPUT.with(|e| e.get()) } -fn unimplemented() -> ! { - unimplemented!("Unimplemented for tests"); +/// For tests: Run the closure recording the syscalls which are invoked in during the run of the closure. +pub fn run_recording_events<R, C: FnMut(&NextReturn) -> R>(mut f: C) -> Vec<Event> { + NEXT_OUTPUT.with(|n| n.set(0)); + NEXT_OUTPUT.with(|n| f(n)); + let mut output = Vec::new(); + EVENTS.with(|e| output.append(&mut e.borrow_mut())); + output +} + +thread_local!(static EVENTS: RefCell<Vec<Event>> = RefCell::new(Vec::new())); +thread_local!(static NEXT_OUTPUT: NextReturn = NextReturn { next_return: Cell::new(0) }); + +#[derive(Clone, Debug, PartialEq)] +/// For tests: syscall event +pub enum Event { + YieldK, + Subscribe( + usize, + usize, + *const unsafe extern "C" fn(usize, usize, usize, usize), + usize, + ), + Command(usize, usize, usize, usize), + Command1(usize, usize, usize), + Allow(usize, usize, *mut u8, usize), + Memop(u32, usize), +} + +/// For tests: controls the next return value of any syscall +pub struct NextReturn { + next_return: Cell<isize>, +} + +impl NextReturn { + /// Set the next return value + pub fn set(&self, value: isize) { + self.next_return.set(value); + } + + fn get(&self) -> isize { + self.next_return.get() + } }