use super::*;
use crate::{
asm,
error::{ExecError, OpError},
memory::MemoryError,
sync::test_util::test_access,
utils::EmptyState,
GasLimit, Op, Vm,
};
#[test]
fn test_memory_store_load() {
let mut memory = Memory::new();
memory.load(0).unwrap_err();
memory.store(0, 0).unwrap_err();
memory.alloc(1).unwrap();
assert_eq!(memory.load(0).unwrap(), 0);
memory.store(0, 1).unwrap();
assert_eq!(memory.load(0).unwrap(), 1);
memory.load(1).unwrap_err();
memory.store(1, 0).unwrap_err();
assert_eq!(memory.len().unwrap(), 1);
}
#[test]
fn test_free_empty_memory() {
let mut memory = Memory::new();
assert!(memory.is_empty());
memory.free(0).unwrap();
}
#[test]
fn test_free_valid_new_len() {
let mut memory = Memory::new();
memory.alloc(10).unwrap();
assert_eq!(memory.len().unwrap(), 10);
for i in 0..10 {
memory.store(i, i as Word).unwrap();
}
memory.free(5).unwrap();
assert_eq!(memory.len().unwrap(), 5);
for i in 0..5 {
assert_eq!(memory.load(i).unwrap(), i as Word);
}
assert!(matches!(memory.load(5), Err(MemoryError::IndexOutOfBounds)));
}
#[test]
fn test_free_at_last_index() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
memory.free(4).unwrap();
assert_eq!(memory.len().unwrap(), 4);
for i in 0..4 {
assert_eq!(memory.load(i).unwrap(), 0);
}
}
#[test]
fn test_free_at_start() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
memory.free(0).unwrap();
assert!(memory.is_empty());
assert_eq!(memory.len().unwrap(), 0);
}
#[test]
fn test_free_invalid_new_len() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
assert!(matches!(memory.free(6), Err(MemoryError::IndexOutOfBounds)));
assert!(matches!(
memory.free(Word::MAX),
Err(MemoryError::IndexOutOfBounds)
));
assert_eq!(memory.len().unwrap(), 5);
}
#[test]
fn test_free_negative_new_len() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
assert!(matches!(
memory.free(-1),
Err(MemoryError::IndexOutOfBounds)
));
assert_eq!(memory.len().unwrap(), 5);
}
#[test]
fn test_free_multiple_times() {
let mut memory = Memory::new();
memory.alloc(10).unwrap();
memory.free(8).unwrap();
assert_eq!(memory.len().unwrap(), 8);
memory.free(5).unwrap();
assert_eq!(memory.len().unwrap(), 5);
memory.free(0).unwrap();
assert!(memory.is_empty());
}
#[test]
fn test_free_then_allocate() {
let mut memory = Memory::new();
memory.alloc(10).unwrap();
memory.free(5).unwrap();
assert_eq!(memory.len().unwrap(), 5);
memory.alloc(3).unwrap();
assert_eq!(memory.len().unwrap(), 8);
for i in 0..5 {
assert_eq!(memory.load(i).unwrap(), 0);
}
}
#[test]
fn test_free_capacity_reduction() {
let mut memory = Memory::new();
memory.alloc(1000).unwrap();
let index_to_keep = 100;
memory.free(index_to_keep).unwrap();
assert_eq!(memory.0.capacity(), index_to_keep as usize);
}
#[test]
fn test_store_range_empty_memory() {
let mut memory = Memory::new();
let values = vec![1, 2, 3];
assert!(matches!(
memory.store_range(0, &values),
Err(MemoryError::IndexOutOfBounds)
));
}
#[test]
fn test_store_range_sanity() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
let values = vec![10, 20, 30];
memory.store_range(0, &values).unwrap();
assert_eq!(memory.load(0).unwrap(), 10);
assert_eq!(memory.load(1).unwrap(), 20);
assert_eq!(memory.load(2).unwrap(), 30);
assert_eq!(memory.load(3).unwrap(), 0);
assert_eq!(memory.load(4).unwrap(), 0);
}
#[test]
fn test_store_range_at_offset() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
let values = vec![10, 20];
memory.store_range(2, &values).unwrap();
assert_eq!(memory.load(0).unwrap(), 0);
assert_eq!(memory.load(1).unwrap(), 0);
assert_eq!(memory.load(2).unwrap(), 10);
assert_eq!(memory.load(3).unwrap(), 20);
assert_eq!(memory.load(4).unwrap(), 0);
}
#[test]
fn test_store_range_exact_fit() {
let mut memory = Memory::new();
memory.alloc(3).unwrap();
let values = vec![1, 2, 3];
memory.store_range(0, &values).unwrap();
for i in 0..3 {
assert_eq!(memory.load(i as Word).unwrap(), (i + 1) as Word);
}
}
#[test]
fn test_store_range_overflow() {
let mut memory = Memory::new();
memory.alloc(3).unwrap();
let values = vec![1, 2, 3, 4];
assert!(matches!(
memory.store_range(0, &values),
Err(MemoryError::IndexOutOfBounds)
));
assert_eq!(memory.load(0).unwrap(), 0);
}
#[test]
fn test_store_range_invalid_start_address() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
let values = vec![1, 2];
assert!(matches!(
memory.store_range(4, &values), Err(MemoryError::IndexOutOfBounds)
));
assert!(matches!(
memory.store_range(5, &values),
Err(MemoryError::IndexOutOfBounds)
));
assert!(matches!(
memory.store_range(Word::MAX, &values),
Err(MemoryError::IndexOutOfBounds)
));
}
#[test]
fn test_store_range_negative_address() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
let values = vec![1, 2];
assert!(matches!(
memory.store_range(-1, &values),
Err(MemoryError::IndexOutOfBounds)
));
assert_eq!(memory.load(0).unwrap(), 0);
}
#[test]
fn test_store_range_empty_slice() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
let values: Vec<Word> = vec![];
memory.store_range(0, &values).unwrap();
assert_eq!(memory.load(0).unwrap(), 0);
}
#[test]
fn test_store_range_multiple_times() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
let values1 = vec![1, 2];
memory.store_range(0, &values1).unwrap();
let values2 = vec![3, 4];
memory.store_range(1, &values2).unwrap();
assert_eq!(memory.load(0).unwrap(), 1);
assert_eq!(memory.load(1).unwrap(), 3);
assert_eq!(memory.load(2).unwrap(), 4);
assert_eq!(memory.load(3).unwrap(), 0);
assert_eq!(memory.load(4).unwrap(), 0);
}
#[test]
fn test_store_range_max_values() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
let values = vec![Word::MAX, Word::MIN, Word::MAX];
memory.store_range(1, &values).unwrap();
assert_eq!(memory.load(1).unwrap(), Word::MAX);
assert_eq!(memory.load(2).unwrap(), Word::MIN);
assert_eq!(memory.load(3).unwrap(), Word::MAX);
}
#[test]
fn test_store_range_after_free() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
memory.free(3).unwrap();
let values = vec![1, 2];
assert!(matches!(
memory.store_range(2, &values),
Err(MemoryError::IndexOutOfBounds)
));
memory.store_range(0, &values).unwrap();
assert_eq!(memory.load(0).unwrap(), 1);
assert_eq!(memory.load(1).unwrap(), 2);
}
#[test]
fn test_load_range_empty_memory() {
let memory = Memory::new();
assert!(matches!(
memory.load_range(0, 1),
Err(MemoryError::IndexOutOfBounds)
));
}
#[test]
fn test_load_range_sanity() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
let test_values = vec![10, 20, 30];
memory.store_range(0, &test_values).unwrap();
let loaded = memory.load_range(0, 3).unwrap();
assert_eq!(loaded, test_values);
}
#[test]
fn test_load_range_at_offset() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
memory.store_range(0, &[1, 2, 3, 4, 5]).unwrap();
let loaded = memory.load_range(2, 2).unwrap();
assert_eq!(loaded, vec![3, 4]);
}
#[test]
fn test_load_range_exact_size() {
let mut memory = Memory::new();
memory.alloc(3).unwrap();
memory.store_range(0, &[1, 2, 3]).unwrap();
let loaded = memory.load_range(0, 3).unwrap();
assert_eq!(loaded, vec![1, 2, 3]);
}
#[test]
fn test_load_range_overflow() {
let mut memory = Memory::new();
memory.alloc(3).unwrap();
assert!(matches!(
memory.load_range(0, 4),
Err(MemoryError::IndexOutOfBounds)
));
assert!(matches!(
memory.load_range(2, 2),
Err(MemoryError::IndexOutOfBounds)
));
}
#[test]
fn test_load_range_invalid_start_address() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
assert!(matches!(
memory.load_range(5, 1),
Err(MemoryError::IndexOutOfBounds)
));
assert!(matches!(
memory.load_range(Word::MAX, 1),
Err(MemoryError::IndexOutOfBounds)
));
}
#[test]
fn test_load_range_negative_address() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
assert!(matches!(
memory.load_range(-1, 1),
Err(MemoryError::IndexOutOfBounds)
));
}
#[test]
fn test_load_range_zero_size() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
let loaded = memory.load_range(0, 0).unwrap();
assert!(loaded.is_empty());
}
#[test]
fn test_load_range_negative_size() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
assert!(matches!(
memory.load_range(0, -1),
Err(MemoryError::Overflow)
));
}
#[test]
fn test_load_range_maximum_size() {
let mut memory = Memory::new();
let size = 100; memory.alloc(size).unwrap();
for i in 0..size {
memory.store(i as Word, i as Word).unwrap();
}
let loaded = memory.load_range(0, size as Word).unwrap();
for (i, &value) in loaded.iter().enumerate() {
assert_eq!(value, i as Word);
}
}
#[test]
fn test_load_range_after_modification() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
memory.store_range(0, &[1, 2, 3, 4, 5]).unwrap();
memory.store(2, 30).unwrap();
memory.store(3, 40).unwrap();
let loaded = memory.load_range(1, 3).unwrap();
assert_eq!(loaded, vec![2, 30, 40]);
}
#[test]
fn test_load_range_after_free() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
memory.store_range(0, &[1, 2, 3, 4, 5]).unwrap();
memory.free(3).unwrap();
assert!(matches!(
memory.load_range(2, 2),
Err(MemoryError::IndexOutOfBounds)
));
let loaded = memory.load_range(0, 2).unwrap();
assert_eq!(loaded, vec![1, 2]);
}
#[test]
fn test_load_range_large_size_overflow() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
assert!(matches!(
memory.load_range(Word::MAX - 1, 2),
Err(MemoryError::IndexOutOfBounds)
));
}
#[test]
fn test_load_range_consecutive_loads() {
let mut memory = Memory::new();
memory.alloc(5).unwrap();
memory.store_range(0, &[1, 2, 3, 4, 5]).unwrap();
let first = memory.load_range(0, 2).unwrap();
let second = memory.load_range(2, 2).unwrap();
let third = memory.load_range(4, 1).unwrap();
assert_eq!(first, vec![1, 2]);
assert_eq!(second, vec![3, 4]);
assert_eq!(third, vec![5]);
}
#[test]
fn test_memory_alloc_store_load_ops() {
let ops = &[
asm::Stack::Push(5).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(42).into(),
asm::Stack::Push(0).into(),
asm::Memory::Store.into(),
asm::Stack::Push(0).into(),
asm::Memory::Load.into(),
];
let op_gas_cost = &|_: &Op| 1;
let mut vm = Vm::default();
vm.exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
assert_eq!(&vm.stack[..], &[42]);
}
#[test]
fn test_memory_store_load_range_ops() {
let ops = &[
asm::Stack::Push(5).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(1).into(), asm::Stack::Push(2).into(),
asm::Stack::Push(3).into(),
asm::Stack::Push(3).into(), asm::Stack::Push(0).into(), asm::Memory::StoreRange.into(),
asm::Stack::Push(0).into(), asm::Stack::Push(3).into(), asm::Memory::LoadRange.into(),
];
let op_gas_cost = &|_: &Op| 1;
let mut vm = Vm::default();
vm.exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
assert_eq!(&vm.stack[..], &[1, 2, 3]);
}
#[test]
fn test_memory_free_ops() {
let ops = &[
asm::Stack::Push(5).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(3).into(),
asm::Memory::Free.into(),
asm::Stack::Push(4).into(),
asm::Memory::Load.into(),
];
let op_gas_cost = &|_: &Op| 1;
let result = Vm::default().exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
);
match result {
Err(ExecError(_, OpError::Memory(MemoryError::IndexOutOfBounds))) => {}
_ => panic!("Expected IndexOutOfBounds error, got {:?}", result),
}
}
#[test]
fn test_memory_store_range_bug_ops() {
let ops = &[
asm::Stack::Push(5).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(99).into(), asm::Stack::Push(1).into(), asm::Stack::Push(2).into(), asm::Memory::StoreRange.into(),
asm::Stack::Push(2).into(), asm::Memory::Load.into(),
];
let op_gas_cost = &|_: &Op| 1;
let mut vm = Vm::default();
vm.exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
assert_eq!(&vm.stack[..], &[99]);
}
#[test]
fn test_memory_load_range_zero_size_ops() {
let ops = &[
asm::Stack::Push(5).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(2).into(), asm::Stack::Push(0).into(), asm::Memory::LoadRange.into(),
];
let op_gas_cost = &|_: &Op| 1;
let mut vm = Vm::default();
vm.exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
let expected: &[i64] = &[];
assert_eq!(&vm.stack[..], expected);
}
#[test]
fn test_memory_store_range_invalid_address_ops() {
let ops = &[
asm::Stack::Push(3).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(2).into(), asm::Stack::Push(1).into(), asm::Stack::Push(2).into(),
asm::Stack::Push(2).into(), asm::Memory::StoreRange.into(),
];
let op_gas_cost = &|_: &Op| 1;
let result = Vm::default().exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
);
match result {
Err(ExecError(_, OpError::Memory(MemoryError::IndexOutOfBounds))) => {}
_ => panic!("Expected IndexOutOfBounds error, got {:?}", result),
}
}
#[test]
fn test_memory_load_range_overflow_ops() {
let ops = &[
asm::Stack::Push(5).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(6).into(), asm::Stack::Push(0).into(), asm::Memory::LoadRange.into(),
];
let op_gas_cost = &|_: &Op| 1;
let result = Vm::default().exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
);
match result {
Err(ExecError(_, OpError::Memory(MemoryError::IndexOutOfBounds))) => {}
_ => panic!("Expected IndexOutOfBounds error, got {:?}", result),
}
}
#[test]
fn test_memory_alloc_free_with_ops() {
let ops = &[
asm::Stack::Push(10).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(5).into(),
asm::Memory::Free.into(),
asm::Stack::Push(7).into(),
asm::Memory::Load.into(),
];
let op_gas_cost = &|_: &Op| 1;
let result = Vm::default().exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
);
match result {
Err(ExecError(_, OpError::Memory(MemoryError::IndexOutOfBounds))) => {}
_ => panic!("Expected IndexOutOfBounds error, got {:?}", result),
}
}
#[test]
fn test_memory_store_range_after_free_ops() {
let ops = &[
asm::Stack::Push(5).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(3).into(),
asm::Memory::Free.into(),
asm::Stack::Push(2).into(), asm::Stack::Push(2).into(), asm::Stack::Push(1).into(), asm::Stack::Push(2).into(),
asm::Memory::StoreRange.into(),
asm::Stack::Push(2).into(), asm::Memory::Load.into(),
asm::Stack::Push(3).into(), asm::Memory::Load.into(),
];
let op_gas_cost = &|_: &Op| 1;
let result = Vm::default().exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
);
match result {
Err(ExecError(_, OpError::Memory(MemoryError::IndexOutOfBounds))) => {}
_ => panic!("Expected IndexOutOfBounds error, got {:?}", result),
}
}
#[test]
fn test_memory_store_range_empty_values_ops() {
let ops = &[
asm::Stack::Push(5).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(0).into(), asm::Stack::Push(0).into(), asm::Memory::StoreRange.into(),
];
let op_gas_cost = &|_: &Op| 1;
let mut vm = Vm::default();
vm.exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
assert!(vm.stack.is_empty());
}
#[test]
fn test_memory_load_store_range_with_ops() {
let ops = &[
asm::Stack::Push(10).into(),
asm::Memory::Alloc.into(),
asm::Stack::Pop.into(),
asm::Stack::Push(1).into(), asm::Stack::Push(2).into(),
asm::Stack::Push(3).into(),
asm::Stack::Push(4).into(),
asm::Stack::Push(5).into(),
asm::Stack::Push(5).into(), asm::Stack::Push(5).into(), asm::Memory::StoreRange.into(),
asm::Stack::Push(5).into(), asm::Stack::Push(5).into(), asm::Memory::LoadRange.into(),
];
let op_gas_cost = &|_: &Op| 1;
let mut vm = Vm::default();
vm.exec_ops(
ops,
test_access().clone(),
&EmptyState,
op_gas_cost,
GasLimit::UNLIMITED,
)
.unwrap();
assert_eq!(&vm.stack[..], &[1, 2, 3, 4, 5]);
}