use turkos_os_init_manifest::{Budget, Entry, Manifest}; use turkos_os_init_startup::{Adapter, Cause, Error, Plan, Rule, Supervisor}; fn entry(id: u16) -> Entry { Entry {id, required_abi: 1, image_sha256: [id as u8; 32], pages: 1, handles: 1} } fn plan() -> Plan<3> { let manifest = Manifest::new([entry(3), entry(1), entry(2)], Budget {pages: 3, handles: 3}).validate().unwrap(); Plan::compile(manifest, [ Rule {id: 3, after: Some(2)}, Rule {id: 1, after: None}, Rule {id: 2, after: Some(1)}, ]).unwrap() } #[derive(Default)] struct Fake { events: Vec<(char, u16)>, fail_start: Option, fail_stop: Option, } impl Adapter for Fake { type Handle = u16; fn start(&mut self, entry: &Entry) -> Result { self.events.push(('s', entry.id)); if self.fail_start == Some(entry.id) { Err(Cause::Injected) } else { Ok(entry.id) } } fn stop(&mut self, handle: &mut u16) -> Result<(), Cause> { self.events.push(('x', *handle)); if self.fail_stop == Some(*handle) { Err(Cause::Busy) } else { Ok(()) } } } #[test] fn os_init_startup_order_positive() { let mut supervisor = Supervisor::new(plan()); let mut adapter = Fake::default(); assert_eq!(supervisor.start_next(&mut adapter), Ok(Some(1))); assert_eq!(supervisor.start_next(&mut adapter), Ok(Some(2))); assert_eq!(supervisor.start_next(&mut adapter), Ok(Some(3))); assert_eq!(supervisor.start_next(&mut adapter), Ok(None)); assert_eq!(supervisor.owner_count(), 3); supervisor.begin_shutdown(); assert_eq!(supervisor.rollback_next(&mut adapter), Ok(Some(3))); assert_eq!(supervisor.rollback_next(&mut adapter), Ok(Some(2))); assert_eq!(supervisor.rollback_next(&mut adapter), Ok(Some(1))); assert_eq!(supervisor.rollback_next(&mut adapter), Ok(None)); assert_eq!(supervisor.owner_count(), 0); assert_eq!(adapter.events, [ ('s', 1), ('s', 2), ('s', 3), ('x', 3), ('x', 2), ('x', 1)]); } #[test] fn os_init_startup_rejects_missing_and_cycle() { let manifest = || Manifest::new([entry(1), entry(2)], Budget {pages: 2, handles: 2}).validate().unwrap(); assert!(matches!(Plan::compile(manifest(), [ Rule {id: 1, after: Some(9)}, Rule {id: 2, after: None}, ]), Err(Error::MissingDependency {service: 1, dependency: 9}))); assert!(matches!(Plan::compile(manifest(), [ Rule {id: 1, after: Some(2)}, Rule {id: 2, after: Some(1)}, ]), Err(Error::Cycle))); assert!(matches!(Plan::compile(manifest(), [ Rule {id: 2, after: None}, Rule {id: 1, after: None}, ]), Err(Error::RuleMismatch(0)))); } #[test] fn os_init_startup_failure_retains_owner_until_reverse_rollback() { let mut supervisor = Supervisor::new(plan()); let mut adapter = Fake {fail_start: Some(2), ..Fake::default()}; assert_eq!(supervisor.start_next(&mut adapter), Ok(Some(1))); assert_eq!(supervisor.start_next(&mut adapter), Err(Error::Start {service: 2, cause: Cause::Injected})); assert_eq!(supervisor.owner_count(), 1); assert_eq!(supervisor.start_next(&mut adapter), Err(Error::RecoveryRequired)); adapter.fail_stop = Some(1); assert_eq!(supervisor.rollback_next(&mut adapter), Err(Error::Stop {service: 1, cause: Cause::Busy})); assert_eq!(supervisor.owner_count(), 1); adapter.fail_stop = None; assert_eq!(supervisor.rollback_next(&mut adapter), Ok(Some(1))); assert_eq!(supervisor.rollback_next(&mut adapter), Ok(None)); assert_eq!(supervisor.owner_count(), 0); assert!(!supervisor.recovering()); }