diff options
| Commit | a6a5cf874fc03296c01bb72abdcb564e5d65ddcd (patch) | |
|---|---|---|
| Author | 2025-02-15 10:29:05 +0200 | |
| Committer | 2025-02-15 10:29:05 +0200 | |
| Files | eb585f078e928748dad4e2c7c3b8f2f19bbcaf82 /vm/src | |
| tonkobali-a6a5cf874fc03296c01bb72abdcb564e5d65ddcd.tar tonkobali-a6a5cf874fc03296c01bb72abdcb564e5d65ddcd.tar.gz tonkobali-a6a5cf874fc03296c01bb72abdcb564e5d65ddcd.zip | ||
| Parent | 448f86e335b8ecd4699c2eadc03fc97ca0bdc19f (diff) | |
test(vm): Add tests
4 files changed, 287 insertions, 10 deletions
| -rw-r--r-- | vm/src/vm/memory.rs | 30 | ||||
| -rw-r--r-- | vm/src/vm/mod.rs | 49 | ||||
| -rw-r--r-- | vm/src/vm/primitive.rs | 105 | ||||
| -rw-r--r-- | vm/src/vmm/command.rs | 113 |
diff --git a/vm/src/vm/memory.rs b/vm/src/vm/memory.rs index 0a6b47c..06bf3f5 100644 --- a/vm/src/vm/memory.rs +++ b/vm/src/vm/memory.rs @@ -146,6 +146,27 @@ mod tests { } #[test] + fn new_memory_inits_spa() { + let mem = Memory::new(); + assert_eq!(mem[spa::Z], 0); + assert_eq!(mem[spa::E], 1); + assert_eq!(mem[spa::N], -1); + assert_eq!(mem[spa::PROGRAM_COUNTER], 0); + assert_eq!(mem[spa::STACK_POINTER], spa::STACK_POINTER as Word); + } + + #[test] + fn new_memory_from_data_inits_spa() { + let data = vec![ 10, 11, 12, 13, 14, 15 ]; + let mem = Memory::new_from(&data); + assert_eq!(mem[spa::Z], 0); + assert_eq!(mem[spa::E], 1); + assert_eq!(mem[spa::N], -1); + assert_eq!(mem[spa::PROGRAM_COUNTER], data[spa::PROGRAM_COUNTER]); + assert_eq!(mem[spa::STACK_POINTER], spa::STACK_POINTER as Word); + } + + #[test] fn parse_63bit_address_correct() { /* #|-- Address --| */ let value: Word = 0b0111100010000010000000000001111100010101001000000000000100001001u64 as Word; @@ -164,4 +185,13 @@ mod tests { assert_eq!(out[1], 0b000001111100010101001, "Second address is incorrect"); assert_eq!(out[2], 0b000000000000100001001, "Third address is incorrect"); } + + #[test] + fn get_mut_creates_single_page() { + let addr = 10246; + let mut mem = Memory::new(); + mem.get_mut(addr); + assert!(mem.sparse_pages.contains_key(&(addr / PAGE_SIZE))); + assert_eq!(mem.sparse_pages.len(), 2, "All pages between 0 and address have been created!"); + } } diff --git a/vm/src/vm/mod.rs b/vm/src/vm/mod.rs index cec5eee..a3af5d1 100644 --- a/vm/src/vm/mod.rs +++ b/vm/src/vm/mod.rs @@ -119,3 +119,52 @@ impl VirtualMachineInterface for VirtualMachine { vm.get_word(index) } } + +#[cfg(test)] +mod tests { + use super::*; + use memory::spa; + + #[test] + fn start_executes_entire_program() { + let vm = create_with_memory(0, &vec![ + /* 0: */ 0, 0, 0, 5, 0, + /* 5: */ 1, 0, 8, + /* 8: */ 0, 1, 0, + /* 11: */ 0, 0, 0, + ]); + vm.spawn(); + thread::sleep(time::Duration::from_millis(200)); + + assert!(vm.is_halted()); + assert_eq!(vm.get_word(spa::Z), 0); + assert_eq!(vm.get_word(spa::E), 2); + assert_eq!(vm.get_word(spa::PROGRAM_COUNTER), 0); + } + + #[test] + fn single_step_executes_single_instruction() { + let mut vm = create_with_memory(0, &vec![ + /* 0: */ 0, 0, 0, 5, 0, + /* 5: */ 1, 0, 8, + /* 8: */ 0, 1, 0, + /* 11: */ 0, 0, 0, + ]); + vm.set_single_step(true); + vm.spawn(); + thread::sleep(time::Duration::from_millis(100)); + + assert!(vm.is_halted()); + assert_eq!(vm.get_word(spa::Z), -1); + assert_eq!(vm.get_word(spa::E), 1); + assert_eq!(vm.get_word(spa::PROGRAM_COUNTER), 8); + + vm.step(); + thread::sleep(time::Duration::from_millis(100)); + + assert!(vm.is_halted()); + assert_eq!(vm.get_word(spa::Z), -1); + assert_eq!(vm.get_word(spa::E), 2); + assert_eq!(vm.get_word(spa::PROGRAM_COUNTER), 11); + } +} diff --git a/vm/src/vm/primitive.rs b/vm/src/vm/primitive.rs index ff17b4d..d995870 100644 --- a/vm/src/vm/primitive.rs +++ b/vm/src/vm/primitive.rs @@ -23,7 +23,6 @@ macro_rules! above { impl PrimitiveVirtualMachine { pub fn new(tick_gap: u64) -> PrimitiveVirtualMachine { PrimitiveVirtualMachine { - halted: false, tick_gap, ..Default::default() } @@ -31,10 +30,9 @@ impl PrimitiveVirtualMachine { pub fn new_with_memory(tick_gap: u64, mem: &Vec<Word>) -> PrimitiveVirtualMachine { PrimitiveVirtualMachine { - halted: false, - single_step: false, tick_gap, mem: Memory::new_from(mem), + ..Default::default() } } @@ -134,3 +132,104 @@ impl PrimitiveVirtualMachine { self.mem.get_mut(spa::PROGRAM_COUNTER) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn new_primitive_vm_no_halt_no_step() { + let vm = PrimitiveVirtualMachine::new(100); + assert!(!vm.halted); + assert!(!vm.single_step); + } + + #[test] + fn new_with_memory_primitive_vm_no_halt_no_step() { + let vm = PrimitiveVirtualMachine::new_with_memory(100, &vec![]); + assert!(!vm.halted); + assert!(!vm.single_step); + } + + #[test] + fn run_subtracts() { + let mut vm = PrimitiveVirtualMachine::new_with_memory(0, &vec![ + /* 0: */ 0, 0, 0, 5, 0, + /* 5: */ 1, 50, 8, + ]); + vm.step(); + assert_eq!(vm.mem[50], -1); + } + + #[test] + fn run_jumps_on_zero() { + let mut vm = PrimitiveVirtualMachine::new_with_memory(0, &vec![ + /* 0: */ 0, 0, 0, 5, 0, + /* 5: */ 1, 2, 100, + ]); + vm.step(); + assert_eq!(vm.mem[spa::PROGRAM_COUNTER], 100); + } + + #[test] + fn run_jumps_on_neg() { + let mut vm = PrimitiveVirtualMachine::new_with_memory(0, &vec![ + /* 0: */ 0, 0, 0, 5, 0, + /* 5: */ 1, 0, 100, + ]); + vm.step(); + assert_eq!(vm.mem[spa::PROGRAM_COUNTER], 100); + } + + #[test] + fn run_doesnt_jump_on_pos() { + let mut vm = PrimitiveVirtualMachine::new_with_memory(0, &vec![ + /* 0: */ 0, 0, 0, 5, 0, + /* 5: */ 2, 1, 100, + ]); + vm.step(); + assert_eq!(vm.mem[spa::PROGRAM_COUNTER], 8); + } + + #[test] + fn run_executes_entire_program() { + let mut vm = PrimitiveVirtualMachine::new_with_memory(0, &vec![ + /* 0: */ 0, 0, 0, 5, 0, + /* 5: */ 1, 0, 8, + /* 8: */ 0, 1, 0, + /* 11: */ 0, 0, 0, + ]); + + vm.step(); + assert!(!vm.halted); + assert_eq!(vm.mem[spa::Z], -1); + + vm.step(); + assert!(!vm.halted); + assert_eq!(vm.mem[spa::Z], -1); + assert_eq!(vm.mem[spa::E], 2); + + vm.step(); + assert!(vm.halted); + + assert_eq!(vm.mem[spa::Z], 0); + assert_eq!(vm.mem[spa::E], 2); + assert_eq!(vm.mem[spa::PROGRAM_COUNTER], 0); + } + + #[test] + fn single_step_executes_only_once() { + let mut vm = PrimitiveVirtualMachine::new_with_memory(0, &vec![ + /* 0: */ 0, 0, 0, 5, 0, + /* 5: */ 1, 0, 8, + /* 8: */ 0, 1, 0, + /* 11: */ 0, 0, 0, + ]); + vm.single_step = true; + vm.step(); + assert!(vm.halted); + + assert_eq!(vm.mem[spa::Z], -1); + assert_eq!(vm.mem[spa::PROGRAM_COUNTER], 8); + } +} diff --git a/vm/src/vmm/command.rs b/vm/src/vmm/command.rs index 188030e..22d0ff3 100644 --- a/vm/src/vmm/command.rs +++ b/vm/src/vmm/command.rs @@ -11,14 +11,17 @@ fn expects_n_args(args: &Vec<&str>, n: usize) -> Result<(), String> { } } -fn parse_location(arg: &str, assignment: bool) -> Result<Vec<usize>, String> { +fn parse_location(arg: &str, assignment: bool) -> Result<Vec<Word>, String> { let nums_str = arg.to_string(); let nums = nums_str .split(&['.', ':']) .collect::<Vec<&str>>(); - let start = nums[0].parse::<usize>() + let start = nums[0].parse::<Word>() .map_err(|_| format!("Unknown command '{}'!", nums[0]))?; + if start < 0 { + return Err(format!("Address '{}' cannot be negative!", start)); + } // Single location if nums.len() == 1 { @@ -26,22 +29,25 @@ fn parse_location(arg: &str, assignment: bool) -> Result<Vec<usize>, String> { } // Range location else { - let end = nums[1].parse::<usize>() + let end = nums[1].parse::<Word>() .map_err(|_| format!("Expected location!"))?; if assignment { Ok(vec![start, end]) } // Range addresses + else if end < 0 { + return Err(format!("Address '{}' cannot be negative!", end)); + } else { Ok(Vec::from_iter(start..=end)) } } } -fn assign_location(locs: Vec<usize>, vm: &mut VirtualMachine) { - print_locations(vec![locs[0]], vm); - vm.set_word(locs[0], locs[1] as Word); +fn assign_location(locs: Vec<Word>, vm: &mut VirtualMachine) { + print_locations(vec![locs[0] as usize], vm); + vm.set_word(locs[0] as usize, locs[1] as Word); } fn print_locations(locs: Vec<usize>, vm: &VirtualMachine) { @@ -148,10 +154,103 @@ Memory IO: assign_location(locs, vm); } else { - print_locations(locs, vm); + let mut locs_casted = Vec::new(); + for elem in locs { + let elem = elem.try_into() + .map_err(|_| format!("Expected address, got '{}'!", elem))?; + locs_casted.push(elem); + } + print_locations(locs_casted, vm); } } }, } Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn expect_args_too_few() { + let res = expects_n_args(&vec!["abc", "arg1"], 2); + assert!(res.is_err()); + } + + #[test] + fn expect_args_too_many() { + let res = expects_n_args(&vec!["abc", "arg1", "arg2", "arg3"], 2); + assert!(res.is_err()); + } + + #[test] + fn expect_args_exact() { + let res = expects_n_args(&vec!["abc", "arg1", "arg2"], 2); + assert!(res.is_ok()); + } + + #[test] + fn parse_location_single_positive() { + let res = parse_location("1234", false); + assert!(res.is_ok()); + let res = res.unwrap(); + assert_eq!(res.len(), 1); + assert_eq!(res[0], 1234); + } + + #[test] + fn parse_location_single_negative() { + let res = parse_location("-1234", false); + assert!(res.is_err()); + } + + #[test] + fn parse_location_range_positives() { + let res = parse_location("1002.1011", false); + assert!(res.is_ok()); + + let res = res.unwrap(); + assert_eq!(res.len(), 10); + for i in 1002..=1011 { + assert_eq!(res[i-1002], i as Word); + } + } + + #[test] + fn parse_location_range_negative() { + let res = parse_location("-1002.1011", false); + assert!(res.is_err()); + + let res = parse_location("1002.-1011", false); + assert!(res.is_err()); + } + + #[test] + fn parse_location_assignment_positive() { + let res = parse_location("1829:99", true); + assert!(res.is_ok()); + + let res = res.unwrap(); + assert_eq!(res.len(), 2); + assert_eq!(res[0], 1829); + assert_eq!(res[1], 99); + } + + #[test] + fn parse_location_assignment_negative_address() { + let res = parse_location("-1829:99", true); + assert!(res.is_err()); + } + + #[test] + fn parse_location_assignment_negative_value() { + let res = parse_location("1829:-99", true); + assert!(res.is_ok()); + + let res = res.unwrap(); + assert_eq!(res.len(), 2); + assert_eq!(res[0], 1829); + assert_eq!(res[1], -99); + } +} |
