include!(concat!(env!("OUT_DIR"), "/process_fixture.rs")); include!(concat!(env!("OUT_DIR"), "/spawn_fixture.rs")); use fixture::*; use turkos_os_process_lifecycle::lifecycle::Outcome; use turkos_os_process_lifecycle_owners::{process_table as p, spawn}; use turkos_os_syscall_abi::syscall_abi as v1; use turkos_os_threads::threads; use turkos_os_user_runtime::{crt, executable::Executable, runtime::*}; use turkos_os_user_runtime_abi as abi; type User = Runtime; const ELF: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/actual_runtime.elf")); const MACHO: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/actual_host.macho")); fn setup() -> (User, p::ProcessId, p::ProcessId) { let mut b = UserBackend::new(); let shell = b.init; let child = b.child(shell); b.kernel(); (User::new(b).ok().unwrap(), shell, child) } fn activate(u: &mut User, id: p::ProcessId) { u.fixture_backend().activate(id); } fn executable(available: u64) -> Result, spawn::Error> { let image = unsafe { spawn::TrustedImage::from_kernel(ELF, ELF.len()) }.unwrap(); Executable::new(image, &[b"runtime", b"two"], &[], available) } fn stack() -> (Vec, spawn::Start) { let recipe = executable(2).unwrap().into_recipe(); let start = recipe.start(); let (mut init, _) = spawn_fixture::setup_recipe(recipe); let ticket = init.begin().unwrap(); let _actual = init.prepare(ticket).ok().unwrap(); let (handle, owner) = match init .fixture_bag() .unwrap() .token(spawn::Kind::Stack) .unwrap() { spawn_fixture::Token::Page { handle, owner, .. } => (*handle, *owner), _ => panic!("real stack page"), }; let image = init.fixture_observe(|r| r.runtime_stack_copy(handle, owner)); (image, start) } #[test] fn actual_static_elf_spawn_owned_stack_yields_two_crt_arguments() { let (image, start) = stack(); assert_eq!(start.sp & 15, 0); let crt = crt::Startup::parse(&image, start).unwrap(); assert_eq!(crt.argc(), 2); assert_eq!( crt.arguments().collect::>(), vec![b"runtime".as_slice(), b"two".as_slice()] ); } #[test] fn crt_unaligned_and_outside_owned_stack_refuse_before_any_pointer_read() { let (image, start) = stack(); for sp in [start.sp + 1, 0, u64::MAX, spawn::STACK_TOP] { let mut s = start; s.sp = sp; assert!(crt::Startup::parse(&image, s).is_err()); } assert!(crt::Startup::parse(&image[..4095], start).is_err()); } #[test] fn crt_every_argv_pointer_span_and_null_terminator_are_bounded() { let (image, start) = stack(); let at = (start.argv - spawn::STACK_VA) as usize; for p in [0, start.sp, start.envp, spawn::STACK_TOP, u64::MAX] { let mut bad = image.clone(); bad[at..at + 8].copy_from_slice(&p.to_le_bytes()); assert!(crt::Startup::parse(&bad, start).is_err()); } let mut bad = image.clone(); bad[at + 16..at + 24].copy_from_slice(&1u64.to_le_bytes()); assert!(crt::Startup::parse(&bad, start).is_err()); } #[test] fn crt_rejects_unterminated_text_and_forged_argc_env_header() { let (image, start) = stack(); let mut bad = image.clone(); let text = (start.sp + 128 - spawn::STACK_VA) as usize; bad[text..text + 65].fill(b'x'); assert!(crt::Startup::parse(&bad, start).is_err()); for offset in [0, 8, 16, 24] { let mut bad = image.clone(); let at = (start.sp - spawn::STACK_VA) as usize + offset; bad[at..at + 8].fill(0xff); assert!(crt::Startup::parse(&bad, start).is_err()); } } #[test] fn actual_macos_macho_rejects_before_pid_or_resource_acquisition() { assert_eq!(&MACHO[..4], &[0xcf, 0xfa, 0xed, 0xfe]); let b = UserBackend::new(); let before = b.primary_inventory(); let image = unsafe { spawn::TrustedImage::from_kernel(MACHO, MACHO.len()) }.unwrap(); assert!(Executable::new(image, &[b"runtime", b"two"], &[], 2).is_err()); assert_eq!(b.primary_inventory(), before); assert_eq!(b.runtime_hooks.borrow().acquisitions, 0); assert_eq!(b.record(b.init).id, b.init); } #[test] fn required_v2_image_refuses_v1_before_any_pid_or_page() { let b = UserBackend::new(); let before = b.primary_inventory(); assert!(executable(1).is_err()); assert_eq!(b.primary_inventory(), before); assert_eq!(b.runtime_hooks.borrow().acquisitions, 0); assert_eq!(executable(2).unwrap().required(), 2); } #[test] fn frozen_v1_rejects_each_v2_call_and_still_accepts_v1_version() { let mut frame = turkos_os_exceptions_frame(); for number in [abi::EXIT, abi::ALLOC, abi::FREE] { frame.x[7] = abi::VERSION; frame.x[8] = number; assert_eq!(v1::decode_call(&frame), Err(v1::Errno::NoSys)); frame.x[7] = 1; assert_eq!(v1::decode_call(&frame), Err(v1::Errno::NoSys)); } frame.x[8] = 0; assert_eq!(v1::decode_call(&frame), Ok(v1::Call::Version)); } fn turkos_os_exceptions_frame() -> turkos_os_exceptions::exceptions::Frame { turkos_os_exceptions::exceptions::Frame::EMPTY } #[test] fn v2_wire_full_numbers_signed_exit_lengths_alignment_and_errno_are_explicit() { assert_eq!( abi::decode(2, 3, [0xffff_ffff_ffff_ffd6, 0, 0, 0, 0, 0]), Ok(abi::Request::Exit(-42)) ); for (version, call) in [(1, 3), (0, 4), (2, u64::MAX), (3, 3)] { assert_eq!(abi::decode(version, call, [1; 6]), Err(v1::Errno::NoSys)); } for (bytes, alignment) in [(0, 1), (4097, 16), (1, 0), (1, 3), (1, 8192)] { assert_eq!( abi::decode(2, 4, [bytes, alignment, 0, 0, 0, 0]), Err(v1::Errno::Invalid) ); } assert_eq!( abi::decode(2, 4, [4096, 4096, 0, 0, 0, 0]), Ok(abi::Request::Alloc { bytes: 4096, alignment: 4096 }) ); assert_eq!( v1::AbiResult::decode(v1::AbiResult::Err(v1::Errno::NoMem).encode()) .unwrap() .length(), Err(v1::Errno::NoMem) ); } #[test] fn three_private_vec_like_buffers_use_actual_rw_frames_pins_and_exact_bytes() { let (mut u, _, pid) = setup(); activate(&mut u, pid); let baseline = u.fixture_backend().primary_inventory(); let mut buffers = Vec::new(); for n in 0..3 { let b = u.allocate(32, 16).unwrap(); for j in 0..32 { u.push(&b, (n * 32 + j) as u8).unwrap(); } buffers.push(b); } assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 3, pins: 3 } ); for (n, b) in buffers.iter().enumerate() { for j in 0..32 { assert_eq!(u.read(b, j), Ok((n * 32 + j) as u8)); } assert_eq!(u.push(b, 0), Err(Error::Capacity)); } for b in buffers { u.free(b).ok().unwrap(); } assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 0, pins: 0 } ); assert_eq!(u.fixture_backend().primary_inventory(), baseline); assert!( u.fixture_backend() .raw_pages() .iter() .take(3) .all(|p| p.iter().all(|b| *b == 0)) ); } #[test] fn quota_enomem_precedes_acquisition_and_other_actual_owner_continues() { let (mut u, shell, pid) = setup(); activate(&mut u, pid); let mut buffers = Vec::new(); for _ in 0..3 { buffers.push(u.allocate(1, 1).unwrap()); } let count = u.fixture_backend().runtime_hooks.borrow().acquisitions; assert!(matches!(u.allocate(1, 1), Err(Error::NoMem))); assert_eq!( u.fixture_backend().runtime_hooks.borrow().acquisitions, count ); activate(&mut u, shell); let shell_buf = u.allocate(1, 1).unwrap(); u.push(&shell_buf, 0x7a).unwrap(); assert_eq!(u.read(&shell_buf, 0), Ok(0x7a)); assert_eq!( u.fixture_backend().record(shell).resources.state, threads::State::Running ); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 4, pins: 4 } ); u.free(shell_buf).ok().unwrap(); activate(&mut u, pid); for b in buffers { u.free(b).ok().unwrap(); } } #[test] fn foreign_process_buffer_refuses_before_frame_read_or_write() { let (mut u, shell, pid) = setup(); activate(&mut u, pid); let b = u.allocate(2, 1).unwrap(); u.push(&b, 0x44).unwrap(); activate(&mut u, shell); let callbacks = u.fixture_backend().runtime_hooks.borrow().callbacks; assert_eq!(u.read(&b, 0), Err(Error::Foreign)); assert_eq!(u.push(&b, 0x55), Err(Error::Foreign)); assert_eq!( u.fixture_backend().runtime_hooks.borrow().callbacks, callbacks + 2 ); activate(&mut u, pid); assert_eq!(u.read(&b, 0), Ok(0x44)); u.free(b).ok().unwrap(); } #[test] fn panic_terminal_consumes_once_only_current_and_healthy_shell_keeps_owners() { let (mut u, shell, pid) = setup(); let before = u.fixture_backend().primary_bytes(); activate(&mut u, pid); let b = u.allocate(4, 1).unwrap(); u.push(&b, 1).unwrap(); u.panic().unwrap(); assert_eq!(u.fixture_backend().runtime_hooks.borrow().terminals, 1); assert!(u.panic().is_err()); assert_eq!(u.fixture_backend().runtime_hooks.borrow().terminals, 1); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 0, pins: 0 } ); assert_eq!(u.fixture_backend().record(pid).exit_code, Some(127)); assert_eq!(u.fixture_backend().reap(pid), Outcome::Clean(127)); activate(&mut u, shell); assert_eq!( u.fixture_backend().record(shell).resources.state, threads::State::Running ); assert_eq!(u.fixture_backend().primary_inventory(), [1, 1, 3, 3]); assert_eq!(u.fixture_backend().primary_bytes(), before[..3]); let _inert_handle = b; } #[test] fn whole4096_zero_check_rejects_dirty_last_byte_before_buffer_publication() { let (mut u, _, pid) = setup(); activate(&mut u, pid); u.fixture_backend().runtime_hooks.borrow_mut().dirty_last = true; assert!(matches!(u.allocate(1, 1), Err(Error::Resource))); assert!(u.quarantined()); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 1, pins: 1 } ); assert_eq!(u.exit(0), Err(Error::Quarantined)); } #[test] fn actual_rw_rights_and_duplicate_frame_observation_are_checked() { for mode in 0..2 { let (mut u, _, pid) = setup(); activate(&mut u, pid); let _first = u.allocate(1, 1).unwrap(); if mode == 0 { u.fixture_backend().runtime_hooks.borrow_mut().readonly = true; } else { u.fixture_backend() .runtime_hooks .borrow_mut() .bad_generation = true; } assert!(matches!(u.allocate(1, 1), Err(Error::Resource))); assert!(u.quarantined()); } } #[test] fn acquire_partial_owner_is_retained_and_terminal_cannot_skip_it() { let (mut u, _, pid) = setup(); activate(&mut u, pid); u.fixture_backend().runtime_hooks.borrow_mut().fail_acquire = 2; assert!(matches!(u.allocate(1, 1), Err(Error::Backend))); assert!(u.quarantined()); assert_eq!(u.fixture_token_parts(HeapToken::parts)[0], [true, false]); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 1, pins: 0 } ); assert_eq!(u.panic(), Err(Error::Quarantined)); assert_eq!(u.fixture_backend().runtime_hooks.borrow().terminals, 0); } #[test] fn before_acquire_enomem_has_no_owner_or_generation_effect() { let (mut u, _, pid) = setup(); activate(&mut u, pid); u.fixture_backend().runtime_hooks.borrow_mut().fail_acquire = 1; assert!(matches!(u.allocate(1, 1), Err(Error::NoMem))); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 0, pins: 0 } ); assert!(!u.quarantined()); u.fixture_backend().runtime_hooks.borrow_mut().fail_acquire = 0; let b = u.allocate(1, 1).unwrap(); u.free(b).ok().unwrap(); } #[test] fn partial_release_retains_real_page_without_consumed_pin_and_never_exits() { let (mut u, _, pid) = setup(); activate(&mut u, pid); let b = u.allocate(1, 1).unwrap(); u.fixture_backend().runtime_hooks.borrow_mut().fail_release = 2; let error = u.free(b).err().unwrap(); assert_eq!(error.error, Error::Backend); assert!(error.buffer.is_some()); assert_eq!(u.fixture_token_parts(HeapToken::parts)[0], [true, false]); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 1, pins: 0 } ); assert_eq!(u.panic(), Err(Error::Quarantined)); assert_eq!(u.fixture_backend().runtime_hooks.borrow().terminals, 0); } #[test] fn successful_release_drift_never_returns_fabricated_buffer_or_pin() { let (mut u, _, pid) = setup(); activate(&mut u, pid); let b = u.allocate(1, 1).unwrap(); let h = u.fixture_backend().runtime_hooks.clone(); h.borrow_mut().drift = 5; h.borrow_mut().drift_when = 7; let error = u.free(b).err().unwrap(); assert_eq!(error.error, Error::Context); assert!(error.buffer.is_none()); assert!(u.quarantined()); u.fixture_backend().reset_context(); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 0, pins: 0 } ); assert_eq!(u.panic(), Err(Error::Quarantined)); } #[test] fn pure_wrong_cpu_irq_mask_root_stack_gate_precedes_owner_borrow() { for mode in 1..=5 { let (mut u, _, pid) = setup(); activate(&mut u, pid); u.fixture_backend().bad_context(mode); let hooks = u.fixture_backend().runtime_hooks.clone(); let callbacks = hooks.borrow().callbacks; assert!(u.allocate(1, 1).is_err()); assert!(u.panic().is_err()); assert_eq!(hooks.borrow().callbacks, callbacks); } } #[test] fn process_callback_drift_quarantines_before_next_owner_access() { for drift in 1..=7 { let (mut u, _, pid) = setup(); activate(&mut u, pid); let hooks = u.fixture_backend().runtime_hooks.clone(); hooks.borrow_mut().drift = drift; hooks.borrow_mut().drift_when = 1; assert!(u.allocate(1, 1).is_err()); assert!(u.quarantined()); u.fixture_backend().reset_context(); let calls = hooks.borrow().callbacks; assert_eq!(u.panic(), Err(Error::Quarantined)); assert_eq!(hooks.borrow().callbacks, calls); } } #[test] fn acquire_callback_drift_keeps_genuine_page_pin_before_handle_return() { let (mut u, _, pid) = setup(); activate(&mut u, pid); let h = u.fixture_backend().runtime_hooks.clone(); h.borrow_mut().drift = 5; h.borrow_mut().drift_when = 3; assert!(matches!(u.allocate(1, 1), Err(Error::Context))); assert!(u.quarantined()); assert_eq!(u.fixture_token_parts(HeapToken::parts)[0], [true, true]); u.fixture_backend().reset_context(); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 1, pins: 1 } ); assert_eq!(h.borrow().terminals, 0); } #[test] fn terminal_post_drift_keeps_true_zombie_no_restored_buffer_and_no_second_call() { let (mut u, _, pid) = setup(); activate(&mut u, pid); let b = u.allocate(1, 1).unwrap(); let h = u.fixture_backend().runtime_hooks.clone(); h.borrow_mut().drift = 5; h.borrow_mut().drift_when = 8; assert_eq!(u.panic(), Err(Error::Context)); assert!(u.quarantined()); assert_eq!(h.borrow().terminals, 1); u.fixture_backend().reset_context(); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 0, pins: 0 } ); assert_eq!(u.fixture_backend().record(pid).exit_code, Some(127)); assert_eq!(u.panic(), Err(Error::Quarantined)); assert_eq!(h.borrow().terminals, 1); let _inert_handle = b; } #[test] fn silent_terminal_and_silent_release_success_cannot_publish_completion() { for mode in 0..2 { let (mut u, _, pid) = setup(); activate(&mut u, pid); if mode == 0 { u.fixture_backend() .runtime_hooks .borrow_mut() .silent_terminal = true; assert_eq!(u.panic(), Err(Error::Resource)); } else { let b = u.allocate(1, 1).unwrap(); u.fixture_backend() .runtime_hooks .borrow_mut() .silent_release = true; assert_eq!(u.free(b).err().unwrap().error, Error::Resource); } assert!(u.quarantined()); assert_eq!( u.fixture_backend().record(pid).resources.state, threads::State::Running ); } } #[test] fn constructor_errors_return_actual_backend_and_live_source_bags() { let b = UserBackend::new(); let before = b.primary_inventory(); let (_, b) = Runtime::<_, 0>::new(b).err().unwrap(); assert_eq!(b.primary_inventory(), before); b.bad_context(1); let (_, b) = User::new(b).err().unwrap(); b.reset_context(); assert_eq!(b.primary_inventory(), before); assert!(User::new(b).is_ok()); } #[test] fn drop_performs_no_implicit_heap_or_process_retirement() { let (mut u, _, pid) = setup(); activate(&mut u, pid); let _b = u.allocate(1, 1).unwrap(); let h = u.fixture_backend().runtime_hooks.clone(); drop(u); assert_eq!(h.borrow().releases, 0); assert_eq!(h.borrow().terminals, 0); } #[test] fn seventeen_plus_real_pid_generations_exit_reap_without_lifetime_terminal_exhaustion() { let (mut u, shell, first) = setup(); let mut last = first; for generation in 0..18 { let id = if generation == 0 { first } else { u.fixture_backend().child(shell) }; if generation != 0 { assert_eq!(id.slot(), last.slot()); assert!(id.generation() > last.generation()); } activate(&mut u, id); u.exit(42).unwrap(); assert_eq!(u.fixture_backend().reap(id), Outcome::Clean(42)); assert_eq!(u.fixture_backend().primary_inventory(), [1, 1, 3, 3]); last = id; } assert_eq!(u.fixture_backend().runtime_hooks.borrow().terminals, 18); } #[test] fn v2_exit_requires_exact_sign_extended_i32_wire_without_upper_bit_truncation() { for code in [i32::MIN, -42, -1, 0, 42, i32::MAX] { let wire = code as i64 as u64; assert_eq!( abi::decode(2, 3, [wire, 0, 0, 0, 0, 0]), Ok(abi::Request::Exit(code)) ); } for wire in [ 1u64 << 32, 0x1234_0000_0000_002a, 0x0000_0000_ffff_ffff, 0xffff_ffff_0000_002a, 0x8000_0000, ] { assert_eq!( abi::decode(2, 3, [wire, 0, 0, 0, 0, 0]), Err(v1::Errno::Overflow) ); } } #[test] fn successful_byte_write_drift_keeps_actual_length_and_bytes_before_quarantine() { let (mut u, _, pid) = setup(); activate(&mut u, pid); let b = u.allocate(2, 1).unwrap(); let h = u.fixture_backend().runtime_hooks.clone(); h.borrow_mut().drift = 5; h.borrow_mut().drift_when = 6; assert_eq!(u.push(&b, 0x7a), Err(Error::Context)); assert!(u.quarantined()); assert_eq!(u.fixture_lengths()[0], 1); assert_eq!(u.fixture_backend().raw_pages()[0][0], 0x7a); } #[test] fn stale_and_double_free_wire_replay_preserves_replacement_generation_and_pin() { let (mut u, _, pid) = setup(); activate(&mut u, pid); let original = u.allocate(1, 1).unwrap(); // Only inert adversarial wire-handle bytes are duplicated. No actual Token, // Registry Pin, mutable resource or process bag is cloned. let stale = unsafe { core::ptr::read(&original) }; let double = unsafe { core::ptr::read(&original) }; u.free(original).ok().unwrap(); assert_eq!(u.free(double).err().unwrap().error, Error::Stale); let replacement = u.allocate(1, 1).unwrap(); u.push(&replacement, 0x66).unwrap(); assert_eq!(u.free(stale).err().unwrap().error, Error::Stale); assert_eq!(u.read(&replacement, 0), Ok(0x66)); assert_eq!( u.fixture_backend().heap_inventory(), Inventory { pages: 1, pins: 1 } ); u.free(replacement).ok().unwrap(); } #[test] fn actual_authority_extra_owner_raises_usage_without_increasing_fixed_quota() { let (mut u, shell, pid) = setup(); activate(&mut u, pid); let quota = u.fixture_backend().fixed_quota(pid); let first = u.allocate(1, 1).unwrap(); let second = u.allocate(1, 1).unwrap(); let file = u.fixture_backend().extra_file(pid); activate(&mut u, pid); assert_eq!(u.fixture_backend().fixed_quota(pid), quota); let acquisitions = u.fixture_backend().runtime_hooks.borrow().acquisitions; assert!(matches!(u.allocate(1, 1), Err(Error::NoMem))); assert_eq!( u.fixture_backend().runtime_hooks.borrow().acquisitions, acquisitions ); activate(&mut u, shell); let healthy = u.allocate(1, 1).unwrap(); u.push(&healthy, 0x7a).unwrap(); assert_eq!(u.read(&healthy, 0), Ok(0x7a)); u.free(healthy).ok().unwrap(); activate(&mut u, pid); u.free(first).ok().unwrap(); u.free(second).ok().unwrap(); u.exit(0).unwrap(); assert_eq!(u.fixture_backend().reap(pid), Outcome::Clean(0)); assert_eq!(u.fixture_backend().primary_inventory(), [1, 1, 3, 3]); drop(file); } #[test] fn backend_context_gate_refuses_before_actual_lifecycle_mutable_loan() { let b = UserBackend::new(); b.activate(b.init); b.bad_context(1); let _held = b.life.borrow_mut(); assert_eq!(Backend::process(&b), Err(Error::Context)); assert_eq!( task_ingress(Backend::execution(&b), Backend::owning_cpu(&b)), Err(Error::Context) ); } #[test] fn actual_elf_required_header_magic_and_full_version_refuse_before_owner_acquire() { let b = UserBackend::new(); let before = b.primary_inventory(); let table = u64::from_le_bytes(ELF[32..40].try_into().unwrap()) as usize; let data = u64::from_le_bytes(ELF[table + 56 + 8..table + 56 + 16].try_into().unwrap()) as usize; for mode in 0..3 { let mut bytes = ELF.to_vec(); if mode == 0 { bytes[data] ^= 1; } else { bytes[data + 8..data + 16] .copy_from_slice(&(if mode == 1 { 3u64 } else { (1u64 << 32) | 2 }).to_le_bytes()); } let bytes = Box::leak(bytes.into_boxed_slice()); let image = unsafe { spawn::TrustedImage::from_kernel(bytes, bytes.len()) }.unwrap(); assert!(Executable::new(image, &[b"runtime", b"two"], &[], 2).is_err()); assert_eq!(b.primary_inventory(), before); assert_eq!(b.runtime_hooks.borrow().acquisitions, 0); } } #[test] fn os_user_runtime_alloc_positive() { let (mut runtime, _, child) = setup(); activate(&mut runtime, child); let baseline = runtime.fixture_backend().primary_inventory(); let acquisitions = runtime.fixture_backend().runtime_hooks.borrow().acquisitions; assert!(matches!(runtime.allocate(1, 3), Err(Error::Invalid))); assert_eq!(runtime.fixture_backend().runtime_hooks.borrow().acquisitions, acquisitions); let mut buffers = Vec::new(); for (index, capacity) in [16, 32, 64].into_iter().enumerate() { let buffer = runtime.allocate(capacity, 16).unwrap(); for position in 0..capacity { runtime.push(&buffer, (index as u8) ^ (position as u8)).unwrap(); } buffers.push(buffer); } assert_eq!(runtime.fixture_backend().heap_inventory(), Inventory { pages: 3, pins: 3 }); for (index, buffer) in buffers.iter().enumerate() { for position in 0..[16, 32, 64][index] { assert_eq!(runtime.read(buffer, position), Ok((index as u8) ^ (position as u8))); } } for buffer in buffers { runtime.free(buffer).ok().unwrap(); } assert_eq!(runtime.fixture_backend().heap_inventory(), Inventory { pages: 0, pins: 0 }); assert_eq!(runtime.fixture_backend().primary_inventory(), baseline); } #[test] fn os_user_runtime_alloc_negative() { let (mut runtime, shell, child) = setup(); activate(&mut runtime, child); let baseline = runtime.fixture_backend().primary_inventory(); let buffers: Vec<_> = (0..3).map(|_| runtime.allocate(1, 1).unwrap()).collect(); let acquisitions = runtime.fixture_backend().runtime_hooks.borrow().acquisitions; let full = runtime.fixture_backend().primary_inventory(); assert!(matches!(runtime.allocate(1, 1), Err(Error::NoMem))); assert_eq!(runtime.fixture_backend().runtime_hooks.borrow().acquisitions, acquisitions); assert_eq!(runtime.fixture_backend().primary_inventory(), full); activate(&mut runtime, shell); let healthy = runtime.allocate(8, 8).unwrap(); runtime.push(&healthy, 0x5a).unwrap(); assert_eq!(runtime.read(&healthy, 0), Ok(0x5a)); assert_eq!(runtime.fixture_backend().record(shell).resources.state, threads::State::Running); runtime.free(healthy).ok().unwrap(); activate(&mut runtime, child); for buffer in buffers { runtime.free(buffer).ok().unwrap(); } assert_eq!(runtime.fixture_backend().heap_inventory(), Inventory { pages: 0, pins: 0 }); assert_eq!(runtime.fixture_backend().primary_inventory(), baseline); }