Test cleanup, tests for stack push and frame push

This commit is contained in:
Chris Allen 2024-11-07 15:53:10 -06:00
parent 8aef2652dd
commit e200121c57
2 changed files with 96 additions and 29 deletions

View File

@ -855,6 +855,12 @@ pub enum FromNounError {
AllocationError(#[from] crate::mem::AllocationError), AllocationError(#[from] crate::mem::AllocationError),
} }
impl FromNounError {
pub fn is_alloc_error(&self) -> bool {
matches!(self, FromNounError::AllocationError(_))
}
}
pub type NounableResult<T> = std::result::Result<T, FromNounError>; pub type NounableResult<T> = std::result::Result<T, FromNounError>;
pub trait Nounable { pub trait Nounable {

View File

@ -44,7 +44,7 @@ pub struct MemoryState {
/// Error type for when a potential allocation would cause an OOM error /// Error type for when a potential allocation would cause an OOM error
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct OutOfMemoryError(pub MemoryState); pub struct OutOfMemoryError(pub MemoryState, pub Allocation,);
/// Error type for allocation errors in [NockStack] /// Error type for allocation errors in [NockStack]
#[derive(Debug, Clone, Error)] #[derive(Debug, Clone, Error)]
@ -63,7 +63,7 @@ impl From<AllocationError> for std::io::Error {
pub type AllocResult<T> = core::result::Result<T, AllocationError>; pub type AllocResult<T> = core::result::Result<T, AllocationError>;
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy)]
pub enum ArenaOrientation { pub enum ArenaOrientation {
/// stack_pointer < alloc_pointer /// stack_pointer < alloc_pointer
/// stack_pointer increases on push /// stack_pointer increases on push
@ -77,7 +77,7 @@ pub enum ArenaOrientation {
East, East,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy)]
pub enum AllocationType { pub enum AllocationType {
/// alloc pointer moves /// alloc pointer moves
Alloc, Alloc,
@ -188,8 +188,8 @@ impl NockStack {
AllocationError::CannotAllocateInPreCopy(self.memory_state(size)) AllocationError::CannotAllocateInPreCopy(self.memory_state(size))
} }
fn out_of_memory(&self, words: Option<usize>) -> AllocationError { fn out_of_memory(&self, alloc: Allocation, words: Option<usize>) -> AllocationError {
AllocationError::OutOfMemory(OutOfMemoryError(self.memory_state(words))) AllocationError::OutOfMemory(OutOfMemoryError(self.memory_state(words), alloc))
} }
pub(crate) fn get_alloc_config(&self, alloc_type: AllocationType) -> Allocation { pub(crate) fn get_alloc_config(&self, alloc_type: AllocationType) -> Allocation {
@ -239,6 +239,9 @@ impl NockStack {
// Types of size: word (words: usize) // Types of size: word (words: usize)
/// Check if an allocation of `size` would cause an OOM error /// Check if an allocation of `size` would cause an OOM error
pub fn alloc_would_oom_(&self, alloc: Allocation, words: usize) -> Result<(), AllocationError> { pub fn alloc_would_oom_(&self, alloc: Allocation, words: usize) -> Result<(), AllocationError> {
let memory_state = self.memory_state(Some(words));
println!("alloc_would_oom_: self.memory_state(): {:#?}, self:is_west(): {}", memory_state, self.is_west());
println!("alloc_would_oom_: alloc: {:#?}", alloc);
if self.pc { if self.pc {
return Err(self.cannot_alloc_in_pc(Some(words))); return Err(self.cannot_alloc_in_pc(Some(words)));
} }
@ -299,14 +302,14 @@ impl NockStack {
if target_point <= limit_point { if target_point <= limit_point {
Ok(()) Ok(())
} else { } else {
Err(self.out_of_memory(Some(words))) Err(self.out_of_memory(alloc, Some(words)))
} }
}, },
Direction::Decreasing => { Direction::Decreasing => {
if target_point >= limit_point { if target_point >= limit_point {
Ok(()) Ok(())
} else { } else {
Err(self.out_of_memory(Some(words))) Err(self.out_of_memory(alloc, Some(words)))
} }
}, },
} }
@ -534,11 +537,7 @@ impl NockStack {
/** Bump the alloc pointer for an east frame to make space for an allocation */ /** Bump the alloc pointer for an east frame to make space for an allocation */
unsafe fn raw_alloc_east(&mut self, words: usize) -> AllocResult<*mut u64> { unsafe fn raw_alloc_east(&mut self, words: usize) -> AllocResult<*mut u64> {
// println!("allocating struct, words: {}, is_west: {}", words, self.is_west());
// println!("pc: {}, sp: {}, ap: {}", self.pc, self.stack_pointer as usize, self.alloc_pointer as usize);
// let alloc = self.get_alloc_config(AllocationType::Alloc);
let () = self.alloc_would_oom(AllocationType::Alloc, words)?; let () = self.alloc_would_oom(AllocationType::Alloc, words)?;
// println!("memory_state: {:#?}, alloc: {alloc:#?}, alloc_would_oom: {:?}\n", self.memory_state(Some(words)), would_oom);
if self.pc { if self.pc {
panic!("Allocation during cleanup phase is prohibited."); panic!("Allocation during cleanup phase is prohibited.");
} }
@ -981,7 +980,7 @@ impl NockStack {
// TODO: Basic alloc function // TODO: Basic alloc function
unsafe fn push_east<T>(&mut self) -> AllocResult<*mut T> { unsafe fn push_east<T>(&mut self) -> AllocResult<*mut T> {
let words = word_size_of::<T>(); let words = word_size_of::<T>();
let () = self.alloc_would_oom_(Allocation { orientation: ArenaOrientation::West, alloc_type: AllocationType::Push, pc: self.pc }, words)?; let () = self.alloc_would_oom_(Allocation { orientation: ArenaOrientation::East, alloc_type: AllocationType::Push, pc: self.pc }, words)?;
let ap = if self.pc { let ap = if self.pc {
*(self.prev_alloc_pointer_pointer()) *(self.prev_alloc_pointer_pointer())
} else { } else {
@ -1320,28 +1319,23 @@ mod test {
jets::cold::{test::{make_noun_list, make_test_stack}, NounList, Nounable}, mem::NockStack, noun::D, unifying_equality::test::unifying_equality, jets::cold::{test::{make_noun_list, make_test_stack}, NounList, Nounable}, mem::NockStack, noun::D, unifying_equality::test::unifying_equality,
}; };
// cargo test -- test_noun_list_alloc --nocapture fn test_noun_list_alloc_fn(stack_size: usize, item_count: u64) -> crate::jets::cold::NounableResult<()> {
#[test]
fn test_noun_list_alloc() {
unsafe { unsafe {
// fails at 512, works at 1024 // fails at 512, works at 1024
const STACK_SIZE: usize = 1; // const STACK_SIZE: usize = 1;
println!("TEST_SIZE: {}", STACK_SIZE); // println!("TEST_SIZE: {}", STACK_SIZE);
let mut stack = make_test_stack(STACK_SIZE); let mut stack = make_test_stack(stack_size);
// Stack size 1 works until 15 elements, 14 passes, 15 fails. // Stack size 1 works until 15 elements, 14 passes, 15 fails.
const ITEM_COUNT: u64 = 15; // const ITEM_COUNT: u64 = 15;
let vec = Vec::from_iter(0..ITEM_COUNT); let vec = Vec::from_iter(0..item_count);
let items = vec.iter().map(|&x| D(x)).collect::<Vec<Noun>>(); let items = vec.iter().map(|&x| D(x)).collect::<Vec<Noun>>();
let slice = vec.as_slice(); let slice = vec.as_slice();
let noun_list = make_noun_list(&mut stack, slice).unwrap(); let noun_list = make_noun_list(&mut stack, slice)?;
assert!(!noun_list.0.is_null()); assert!(!noun_list.0.is_null());
// This always reports 16, what gives? let noun = noun_list.into_noun(&mut stack)?;
// let space_needed = noun_list.space_needed(&mut stack);
// assert!(space_needed <= TEST_SIZE, "space_needed = {}, TEST_SIZE: {}", space_needed, TEST_SIZE);
let noun = noun_list.into_noun(&mut stack).unwrap();
let new_noun_list: NounList = let new_noun_list: NounList =
<NounList as Nounable>::from_noun::<NockStack>(&mut stack, &noun).unwrap(); <NounList as Nounable>::from_noun::<NockStack>(&mut stack, &noun)?;
let mut item_count = 0; let mut tracking_item_count = 0;
println!("items: {:?}", items); println!("items: {:?}", items);
for (a, b) in new_noun_list.zip(items.iter()) { for (a, b) in new_noun_list.zip(items.iter()) {
let a_ptr = a; let a_ptr = a;
@ -1354,9 +1348,76 @@ mod test {
a_val, a_val,
b b
); );
item_count += 1; tracking_item_count += 1;
} }
assert_eq!(item_count, ITEM_COUNT as usize); assert_eq!(tracking_item_count, item_count as usize);
} }
Ok(())
}
// cargo test -p sword test_noun_list_alloc -- --nocapture
#[test]
fn test_noun_list_alloc() {
let should_fail_to_alloc = test_noun_list_alloc_fn(1, 15);
assert!(should_fail_to_alloc.map_err(|err| err.is_alloc_error()).unwrap_err());
let should_succeed = test_noun_list_alloc_fn(1, 14);
assert!(should_succeed.is_ok());
}
// cargo test -p sword test_frame_push -- --nocapture
#[test]
fn test_frame_push() {
// fails at 100, passes at 99, top_slots default to 100?
const PASSES: usize = 99;
const FAILS: usize = 100;
let stack_size = 1;
let mut stack = make_test_stack(stack_size);
let frame_push_res = stack.frame_push(FAILS);
assert!(frame_push_res.is_err());
let mut stack = make_test_stack(stack_size);
let frame_push_res = stack.frame_push(PASSES);
assert!(frame_push_res.is_ok());
}
// cargo test -p sword test_stack_push -- --nocapture
#[test]
fn test_stack_push() {
let stack_size = 1;
let mut stack = make_test_stack(stack_size);
let mut counter = 0;
// Fails at 102, probably because top_slots is 100?
while counter < 102 {
let push_res = unsafe { stack.push::<u64>() };
assert!(push_res.is_ok(), "Failed to push, counter: {}", counter);
counter += 1;
}
let push_res = unsafe { stack.push::<u64>() };
assert!(push_res.is_err());
}
// cargo test -p sword test_frame_and_stack_push -- --nocapture
#[test]
fn test_frame_and_stack_push() {
let stack_size = 1;
let mut stack = make_test_stack(stack_size);
let mut counter = 0;
while counter < 20 {
let frame_push_res = stack.frame_push(1);
assert!(frame_push_res.is_ok(), "Failed to frame_push, counter: {}", counter);
let push_res: Result<*mut u64, AllocationError> = unsafe { stack.push::<u64>() };
assert!(push_res.is_ok(), "Failed to push, counter: {}", counter);
counter += 1;
}
let frame_push_res = stack.frame_push(1);
assert!(frame_push_res.is_err());
// a single stack u64 push won't cause an error but a frame push will
let push_res = unsafe { stack.push::<u64>() };
assert!(push_res.is_ok());
// pushing an array of 1 u64 will NOT cause an error
let push_res = unsafe { stack.push::<[u64; 1]>() };
assert!(push_res.is_ok());
// pushing an array of 2 u64s WILL cause an error
let push_res = unsafe { stack.push::<[u64; 2]>() };
assert!(push_res.is_err());
} }
} }