diff --git a/benchmarks/compare/src/main.rs b/benchmarks/compare/src/main.rs index 92e57d2a..f2048619 100644 --- a/benchmarks/compare/src/main.rs +++ b/benchmarks/compare/src/main.rs @@ -84,21 +84,19 @@ fn read_chunks_btreemap(mem_id: u8, n: usize) { // StableVec benchmarks fn write_chunks_vec(mem_id: u8, n: usize) { - let vec: StableVec, _> = - StableVec::new(init_memory(mem_id)).expect("Vec::new failed"); + let vec: StableVec, _> = StableVec::new(init_memory(mem_id)); let chunks: Vec<_> = chunk_data(n).iter().map(|v| BoundedVecN::from(v)).collect(); bench_fn(|| { for chunk in &chunks { - vec.push(chunk).expect("Vec::push failed"); + vec.push(chunk); } }); } fn read_chunks_vec(mem_id: u8, n: usize) { write_chunks_vec::(mem_id, n); - let vec: StableVec, _> = - StableVec::init(init_memory(mem_id)).expect("Vec::init failed"); + let vec: StableVec, _> = StableVec::init(init_memory(mem_id)); bench_fn(|| { for i in 0..n as u64 { diff --git a/benchmarks/vec/src/main.rs b/benchmarks/vec/src/main.rs index 7463ff59..02741875 100644 --- a/benchmarks/vec/src/main.rs +++ b/benchmarks/vec/src/main.rs @@ -91,7 +91,7 @@ fn vec_insert_blob() -> BenchResult { fn vec_insert() -> BenchResult { let num_items = 10_000; - let svec: StableVec = StableVec::new(DefaultMemoryImpl::default()).unwrap(); + let svec: StableVec = StableVec::new(DefaultMemoryImpl::default()); let mut rng = Rng::from_seed(0); let mut random_items = Vec::with_capacity(num_items); @@ -102,7 +102,7 @@ fn vec_insert() -> BenchResult { bench_fn(|| { for item in random_items.iter() { - svec.push(item).unwrap(); + svec.push(item); } }) } @@ -118,12 +118,12 @@ fn vec_get_blob_mem_manager() -> BenchResult { fn vec_get(memory: impl Memory) -> BenchResult { let num_items = 10_000; - let svec: StableVec = StableVec::new(memory).unwrap(); + let svec: StableVec = StableVec::new(memory); let mut rng = Rng::from_seed(0); for _ in 0..num_items { - svec.push(&T::random(&mut rng)).unwrap(); + svec.push(&T::random(&mut rng)); } bench_fn(|| { diff --git a/examples/src/quick_start/src/lib.rs b/examples/src/quick_start/src/lib.rs index 6b0e344b..1faef210 100644 --- a/examples/src/quick_start/src/lib.rs +++ b/examples/src/quick_start/src/lib.rs @@ -39,8 +39,7 @@ fn stable_get(key: u128) -> Option { // if it exists. #[update] fn stable_insert(key: u128, value: u128) -> Option { - STATE - .with(|s| s.borrow_mut().stable_data.insert(key, value)) + STATE.with(|s| s.borrow_mut().stable_data.insert(key, value)) } // Sets the data that lives on the heap. @@ -61,7 +60,8 @@ fn pre_upgrade() { // Serialize the state. // This example is using CBOR, but you can use any data format you like. let mut state_bytes = vec![]; - STATE.with(|s| ciborium::ser::into_writer(&*s.borrow(), &mut state_bytes)) + STATE + .with(|s| ciborium::ser::into_writer(&*s.borrow(), &mut state_bytes)) .expect("failed to encode state"); // Write the length of the serialized bytes to memory, followed by the @@ -89,9 +89,7 @@ fn post_upgrade() { // Deserialize and set the state. let state = ciborium::de::from_reader(&*state_bytes).expect("failed to decode state"); - STATE.with(|s| { - *s.borrow_mut() = state - }); + STATE.with(|s| *s.borrow_mut() = state); } fn init_stable_data() -> StableBTreeMap { @@ -106,4 +104,3 @@ impl Default for State { } } } - diff --git a/examples/src/task_timer/src/lib.rs b/examples/src/task_timer/src/lib.rs index 6d84bb5c..64722d3f 100644 --- a/examples/src/task_timer/src/lib.rs +++ b/examples/src/task_timer/src/lib.rs @@ -14,7 +14,7 @@ thread_local! { MEMORY_MANAGER.with(|mm| RefCell::new( StableMinHeap::init(mm.borrow().get(MemoryId::new(1))) - .expect("failed to initialize the tasks")) + ) ); } @@ -26,11 +26,7 @@ fn post_upgrade() { #[update] fn schedule_task(after_sec: u64) { let task_time = ic_cdk::api::time() + after_sec * 1_000_000_000; - TASKS.with(|t| { - t.borrow_mut() - .push(&task_time) - .expect("failed to schedule a task") - }); + TASKS.with(|t| t.borrow_mut().push(&task_time)); reschedule(); } diff --git a/fuzz/fuzz_targets/stable_minheap_multiple_ops_persistent.rs b/fuzz/fuzz_targets/stable_minheap_multiple_ops_persistent.rs index f6410702..269fda21 100644 --- a/fuzz/fuzz_targets/stable_minheap_multiple_ops_persistent.rs +++ b/fuzz/fuzz_targets/stable_minheap_multiple_ops_persistent.rs @@ -25,7 +25,6 @@ thread_local! { StableMinHeap::init( MEMORY_MANAGER.with(|m| m.borrow().get(MemoryId::new(0))), ) - .expect("Unable to init Bounded StableMinHeap") ); static DIR: TempDir = tempdir().unwrap(); diff --git a/src/cell.rs b/src/cell.rs index 33f672dc..8583efa7 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -96,36 +96,39 @@ pub struct Cell { impl Cell { /// Creates a new cell in the specified memory, overwriting the previous contents of the memory. - pub fn new(memory: M, value: T) -> Result { - Self::flush_value(&memory, &value)?; - Ok(Self { memory, value }) + pub fn new(memory: M, value: T) -> Self { + Self::flush_value(&memory, &value).expect("Failed to write initial value to the memory"); + Self { memory, value } } /// Initializes the value of the cell based on the contents of the `memory`. /// If the memory already contains a cell, initializes the cell with the decoded value. /// Otherwise, sets the cell value to `default_value` and writes it to the memory. - pub fn init(memory: M, default_value: T) -> Result { + pub fn init(memory: M, default_value: T) -> Self { if memory.size() == 0 { - return Ok(Self::new(memory, default_value)?); + return Self::new(memory, default_value); } let header = Self::read_header(&memory); if &header.magic != MAGIC { - return Ok(Self::new(memory, default_value)?); + return Self::new(memory, default_value); } if header.version != LAYOUT_VERSION { - return Err(InitError::IncompatibleVersion { - last_supported_version: LAYOUT_VERSION, - decoded_version: header.version, - }); + panic!( + "Failed to initialize cell: {}", + InitError::IncompatibleVersion { + last_supported_version: LAYOUT_VERSION, + decoded_version: header.version, + } + ); } - Ok(Self { + Self { value: Self::read_value(&memory, header.value_length), memory, - }) + } } /// Reads and decodes the value of specified length. @@ -169,9 +172,9 @@ impl Cell { /// Updates the current value in the cell. /// If the new value is too large to fit into the memory, the value in the cell does not /// change. - pub fn set(&mut self, value: T) -> Result { - Self::flush_value(&self.memory, &value)?; - Ok(std::mem::replace(&mut self.value, value)) + pub fn set(&mut self, value: T) -> T { + Self::flush_value(&self.memory, &value).expect("Failed to write value to the memory"); + std::mem::replace(&mut self.value, value) } /// Writes the value to the memory, growing the memory size if needed. diff --git a/src/cell/tests.rs b/src/cell/tests.rs index b6d44812..acfe8257 100644 --- a/src/cell/tests.rs +++ b/src/cell/tests.rs @@ -1,62 +1,56 @@ -use crate::cell::{Cell, ValueError}; +use crate::cell::Cell; use crate::storable::Storable; use crate::vec_mem::VectorMemory; use crate::{Memory, RestrictedMemory, WASM_PAGE_SIZE}; fn reload(c: Cell) -> Cell { - Cell::init(c.into_memory(), T::default()).unwrap() + Cell::init(c.into_memory(), T::default()) } #[test] fn test_cell_init() { let mem = VectorMemory::default(); - let cell = Cell::init(mem, 1024u64).unwrap(); + let cell = Cell::init(mem, 1024u64); assert_eq!(*cell.get(), 1024u64); let mem = cell.into_memory(); assert_ne!(mem.size(), 0); - let cell = Cell::init(mem, 0u64).unwrap(); + let cell = Cell::init(mem, 0u64); assert_eq!(1024u64, *cell.get()); // Check that Cell::new overwrites the contents unconditionally. - let cell = Cell::new(cell.into_memory(), 2048u64).unwrap(); + let cell = Cell::new(cell.into_memory(), 2048u64); assert_eq!(2048u64, *cell.get()); } #[test] fn test_cell_init_empty() { let mem = VectorMemory::default(); - let cell = Cell::init(mem, vec![]).unwrap(); + let cell = Cell::init(mem, vec![]); assert_eq!(Vec::::new(), *cell.get()); } #[test] -fn test_out_of_space() { +#[should_panic(expected = "ValueTooLarge { value_size: 65536 }")] +fn test_failure_out_of_space() { let mem = RestrictedMemory::new(VectorMemory::default(), 0..1); let data = [1u8; 100]; - let mut cell = Cell::new(mem, data.to_vec()).unwrap(); + let mut cell = Cell::new(mem, data.to_vec()); assert_eq!(&data[..], &cell.get()[..]); - assert_eq!( - Err(ValueError::ValueTooLarge { - value_size: WASM_PAGE_SIZE, - }), - cell.set(vec![2u8; WASM_PAGE_SIZE as usize]) - ); - - assert_eq!(&data[..], &cell.get()[..]); + cell.set(vec![2u8; WASM_PAGE_SIZE as usize]); } #[test] fn test_cell_grow_and_shrink() { let mem = VectorMemory::default(); - let mut cell = Cell::init(mem, vec![1u8; 10]).unwrap(); + let mut cell = Cell::init(mem, vec![1u8; 10]); - cell.set(vec![2u8; 20]).unwrap(); + cell.set(vec![2u8; 20]); let mut cell = reload(cell); assert_eq!(&[2u8; 20][..], &cell.get()[..]); - cell.set(vec![3u8; 5]).unwrap(); + cell.set(vec![3u8; 5]); let cell = reload(cell); assert_eq!(&[3u8; 5][..], &cell.get()[..]); } diff --git a/src/log.rs b/src/log.rs index f6d1d284..015dbd36 100644 --- a/src/log.rs +++ b/src/log.rs @@ -183,33 +183,39 @@ impl Log { /// Initializes the log based on the contents of the provided memory trait objects. /// If the memory trait objects already contain a stable log, this function recovers it from the stable /// memory. Otherwise, this function allocates a new empty log. - pub fn init(index_memory: INDEX, data_memory: DATA) -> Result { + pub fn init(index_memory: INDEX, data_memory: DATA) -> Self { // if the data memory is not containing expected data, the index is useless anyway. if data_memory.size() == 0 { - return Ok(Self::new(index_memory, data_memory)); + return Self::new(index_memory, data_memory); } let data_header = Self::read_header(&data_memory); if &data_header.magic != DATA_MAGIC { - return Ok(Self::new(index_memory, data_memory)); + return Self::new(index_memory, data_memory); } if data_header.version != LAYOUT_VERSION { - return Err(InitError::IncompatibleDataVersion { - last_supported_version: LAYOUT_VERSION, - decoded_version: data_header.version, - }); + panic!( + "Failed to initialize log: {}", + InitError::IncompatibleDataVersion { + last_supported_version: LAYOUT_VERSION, + decoded_version: data_header.version, + } + ); } let index_header = Self::read_header(&index_memory); if &index_header.magic != INDEX_MAGIC { - return Err(InitError::InvalidIndex); + panic!("Failed to initialize log: {}", InitError::InvalidIndex); } if index_header.version != LAYOUT_VERSION { - return Err(InitError::IncompatibleIndexVersion { - last_supported_version: LAYOUT_VERSION, - decoded_version: index_header.version, - }); + panic!( + "Failed to initialize log: {}", + InitError::IncompatibleIndexVersion { + last_supported_version: LAYOUT_VERSION, + decoded_version: index_header.version, + } + ); } #[cfg(debug_assertions)] @@ -217,11 +223,11 @@ impl Log { assert_eq!(Ok(()), Self::validate_v1_index(&index_memory)); } - Ok(Self { + Self { index_memory, data_memory, _marker: PhantomData, - }) + } } /// Writes the stable log header to memory. diff --git a/src/log/tests.rs b/src/log/tests.rs index 43bd0558..545ea72a 100644 --- a/src/log/tests.rs +++ b/src/log/tests.rs @@ -1,4 +1,4 @@ -use crate::log::{iter_thread_local, InitError, Log, WriteError}; +use crate::log::{iter_thread_local, Log, WriteError}; use crate::vec_mem::VectorMemory; use crate::{Memory, RestrictedMemory, WASM_PAGE_SIZE}; use std::cell::RefCell; @@ -9,7 +9,6 @@ thread_local! { fn new_string_log() -> Log { Log::init(VectorMemory::default(), VectorMemory::default()) - .expect("failed to initialize stable log") } #[test] @@ -21,7 +20,7 @@ fn test_log_construct() { assert_eq!(log.index_size_bytes(), 40); let (index_memory, data_memory) = log.into_memories(); - let log = Log::, _, _>::init(index_memory, data_memory).expect("failed to init log"); + let log = Log::, _, _>::init(index_memory, data_memory); assert_eq!(log.len(), 0); assert_eq!(log.log_size_bytes(), 0); assert_eq!(log.index_size_bytes(), 40); @@ -42,8 +41,7 @@ fn test_new_overwrites() { #[test] fn test_log_init_empty() { - let log = Log::, _, _>::init(VectorMemory::default(), VectorMemory::default()) - .expect("failed to init log"); + let log = Log::, _, _>::init(VectorMemory::default(), VectorMemory::default()); assert_eq!(log.len(), 0); assert_eq!(log.log_size_bytes(), 0); @@ -55,28 +53,27 @@ fn test_log_init_with_different_data_magic() { let mem = VectorMemory::default(); assert_eq!(mem.grow(1), 0); mem.write(0, b"WAS"); - let log = Log::, _, _>::init(VectorMemory::default(), mem).expect("failed to init log"); + let log = Log::, _, _>::init(VectorMemory::default(), mem); assert_eq!(log.len(), 0); } #[test] -fn test_log_init_with_different_index_magic() { +#[should_panic(expected = "Invalid index")] +fn test_failure_log_init_with_different_index_magic() { let index_mem = VectorMemory::default(); assert_eq!(index_mem.grow(1), 0); index_mem.write(0, b"WAS"); let data_mem = VectorMemory::default(); assert_eq!(data_mem.grow(1), 0); data_mem.write(0, b"GLD\x01"); - assert_eq!( - Log::, _, _>::init(index_mem, data_mem) - .map(|_| ()) - .unwrap_err(), - InitError::InvalidIndex - ); + Log::, _, _>::init(index_mem, data_mem); } #[test] -fn test_log_load_bad_index_version() { +#[should_panic( + expected = "Incompatible index version: last supported version is 1, but decoded version is 2" +)] +fn test_failure_log_load_bad_index_version() { let index_memory = VectorMemory::default(); assert_eq!(index_memory.grow(1), 0); index_memory.write(0, b"GLI\x02"); @@ -84,32 +81,18 @@ fn test_log_load_bad_index_version() { let data_memory = VectorMemory::default(); assert_eq!(data_memory.grow(1), 0); data_memory.write(0, b"GLD\x01"); - assert_eq!( - Log::, _, _>::init(index_memory, data_memory) - .map(|_| ()) - .unwrap_err(), - InitError::IncompatibleIndexVersion { - last_supported_version: 1, - decoded_version: 2 - }, - ); + Log::, _, _>::init(index_memory, data_memory); } #[test] -fn test_log_load_bad_data_version() { +#[should_panic( + expected = "Incompatible data version: last supported version is 1, but decoded version is 2" +)] +fn test_failure_log_load_bad_data_version() { let mem = VectorMemory::default(); assert_eq!(mem.grow(1), 0); mem.write(0, b"GLD\x02"); - - assert_eq!( - Log::, _, _>::init(VectorMemory::default(), mem) - .map(|_| ()) - .unwrap_err(), - InitError::IncompatibleDataVersion { - last_supported_version: 1, - decoded_version: 2 - }, - ); + Log::, _, _>::init(VectorMemory::default(), mem); } #[test] @@ -138,7 +121,7 @@ fn test_log_append_persistence() { let (index_memory, data_memory) = log.into_memories(); - let log = Log::, _, _>::init(index_memory, data_memory).unwrap(); + let log = Log::, _, _>::init(index_memory, data_memory); assert_eq!(log.len(), 1); assert_eq!(log.get(idx).unwrap(), b"DEADBEEF".to_vec()); assert_eq!(log.log_size_bytes(), b"DEADBEEF".len() as u64); diff --git a/src/min_heap.rs b/src/min_heap.rs index ecd50d15..e2f2c55c 100644 --- a/src/min_heap.rs +++ b/src/min_heap.rs @@ -1,6 +1,6 @@ -use crate::base_vec::{BaseVec, InitError}; +use crate::base_vec::BaseVec; use crate::storable::Storable; -use crate::{GrowFailed, Memory}; +use crate::Memory; use std::fmt; #[cfg(test)] @@ -30,8 +30,10 @@ where /// contained. /// /// Complexity: O(1) - pub fn new(memory: M) -> Result { - BaseVec::::new(memory, MAGIC).map(Self) + pub fn new(memory: M) -> Self { + BaseVec::::new(memory, MAGIC) + .map(Self) + .expect("Failed to create a new heap") } /// Initializes a heap in the specified memory. @@ -40,8 +42,10 @@ where /// /// PRECONDITION: the memory is either empty or contains a valid /// stable heap. - pub fn init(memory: M) -> Result { - BaseVec::::init(memory, MAGIC).map(Self) + pub fn init(memory: M) -> Self { + BaseVec::::init(memory, MAGIC) + .map(Self) + .expect("Failed to initialize a heap") } /// Returns the number of items in the heap. @@ -61,11 +65,10 @@ where /// Pushes an item onto the heap. /// /// Complexity: O(log(self.len())) - pub fn push(&mut self, item: &T) -> Result<(), GrowFailed> { - self.0.push(item)?; + pub fn push(&mut self, item: &T) { + self.0.push(item).expect("heap push failed"); self.bubble_up(self.0.len() - 1, item); debug_assert_eq!(Ok(()), self.check_invariant()); - Ok(()) } /// Removes the smallest item from the heap and returns it. diff --git a/src/min_heap/tests.rs b/src/min_heap/tests.rs index a366d519..1a59e3b2 100644 --- a/src/min_heap/tests.rs +++ b/src/min_heap/tests.rs @@ -1,6 +1,6 @@ -use super::{InitError, MinHeap as StableMinHeap}; +use super::MinHeap as StableMinHeap; use crate::vec_mem::VectorMemory as M; -use crate::{GrowFailed, Memory}; +use crate::Memory; use proptest::collection::vec as pvec; use proptest::prelude::*; use std::cmp::Reverse; @@ -24,12 +24,12 @@ proptest! { #[test] fn push_pop_model_u64(ops in pvec(arb_op(any::()), 40)) { let mut h = BinaryHeap::new(); - let mut sh = StableMinHeap::::new(M::default()).unwrap(); + let mut sh = StableMinHeap::::new(M::default()); for op in ops { match op { Operation::Push(x) => { - sh.push(&x).unwrap(); + sh.push(&x); h.push(Reverse(x)); } Operation::Pop => { @@ -41,9 +41,9 @@ proptest! { #[test] fn pop_sorted(mut items in pvec(any::(), 0..50)) { - let mut sh = StableMinHeap::::new(M::default()).unwrap(); + let mut sh = StableMinHeap::::new(M::default()); for x in &items { - sh.push(x).unwrap(); + sh.push(x); } items.sort(); for x in items { @@ -54,12 +54,12 @@ proptest! { #[test] fn test_simple_case() { - let mut h = StableMinHeap::::new(M::default()).unwrap(); + let mut h = StableMinHeap::::new(M::default()); assert_eq!(h.pop(), None); - h.push(&0).unwrap(); - h.push(&3).unwrap(); - h.push(&0).unwrap(); - h.push(&1).unwrap(); + h.push(&0); + h.push(&3); + h.push(&0); + h.push(&1); assert_eq!(h.pop(), Some(0)); assert_eq!(h.pop(), Some(0)); assert_eq!(h.pop(), Some(1)); @@ -69,62 +69,56 @@ fn test_simple_case() { } #[test] -fn test_init_type_compatibility() { - let h = StableMinHeap::::new(M::default()).unwrap(); - - assert_eq!( - StableMinHeap::::init(h.into_memory()).unwrap_err(), - InitError::IncompatibleElementType - ); +#[should_panic(expected = "IncompatibleElementType")] +fn test_failure_incompatible_element_type() { + let h = StableMinHeap::::new(M::default()); + StableMinHeap::::init(h.into_memory()); } -#[test] -fn test_init_failures() { - struct EmptyMem; - impl Memory for EmptyMem { - fn size(&self) -> u64 { - 0 - } - fn grow(&self, _: u64) -> i64 { - -1 - } - fn read(&self, _: u64, _: &mut [u8]) { - panic!("out of bounds") - } - fn write(&self, _: u64, _: &[u8]) { - panic!("out of bounds") - } +struct EmptyMem; + +impl Memory for EmptyMem { + fn size(&self) -> u64 { + 0 + } + fn grow(&self, _: u64) -> i64 { + -1 + } + fn read(&self, _: u64, _: &mut [u8]) { + panic!("out of bounds") + } + fn write(&self, _: u64, _: &[u8]) { + panic!("out of bounds") } +} - assert_eq!( - StableMinHeap::::new(EmptyMem).unwrap_err(), - GrowFailed { - current_size: 0, - delta: 1 - } - ); +#[test] +#[should_panic(expected = "GrowFailed { current_size: 0, delta: 1 }")] +fn test_failure_new() { + StableMinHeap::::new(EmptyMem); +} - assert_eq!( - StableMinHeap::::init(EmptyMem).unwrap_err(), - InitError::OutOfMemory, - ); +#[test] +#[should_panic(expected = "OutOfMemory")] +fn test_failure_init() { + StableMinHeap::::init(EmptyMem); +} +#[test] +#[should_panic(expected = "BadMagic { actual: [83, 86, 67], expected: [83, 77, 72] }")] +fn test_failure_bad_magic() { + // BadMagic { actual: *b"SVC", expected: *b"SMH" } let mem = M::default(); mem.grow(1); mem.write(0, b"SVC\x01\x08\x00\x00\x00\x00\x00\x00\x00\x01"); - assert_eq!( - StableMinHeap::::init(mem).unwrap_err(), - InitError::BadMagic { - actual: *b"SVC", - expected: *b"SMH" - }, - ); + StableMinHeap::::init(mem); +} +#[test] +#[should_panic(expected = "IncompatibleVersion(15)")] +fn test_failure_incompatible_version() { let mem = M::default(); mem.grow(1); mem.write(0, b"SMH\x0f\x08\x00\x00\x00\x00\x00\x00\x00\x01"); - assert_eq!( - StableMinHeap::::init(mem).unwrap_err(), - InitError::IncompatibleVersion(15), - ); + StableMinHeap::::init(mem); } diff --git a/src/vec.rs b/src/vec.rs index 7e353a7c..5d88e02e 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -3,7 +3,7 @@ use crate::base_vec::BaseVec; pub use crate::base_vec::InitError; use crate::storable::Storable; -use crate::{GrowFailed, Memory}; +use crate::Memory; use std::fmt; #[cfg(test)] @@ -20,8 +20,10 @@ impl Vec { /// contained previously. /// /// Complexity: O(1) - pub fn new(memory: M) -> Result { - BaseVec::::new(memory, MAGIC).map(Self) + pub fn new(memory: M) -> Self { + BaseVec::::new(memory, MAGIC) + .map(Self) + .expect("Failed to create a new vector") } /// Initializes a vector in the specified memory. @@ -30,8 +32,10 @@ impl Vec { /// /// PRECONDITION: the memory is either empty or contains a valid /// stable vector. - pub fn init(memory: M) -> Result { - BaseVec::::init(memory, MAGIC).map(Self) + pub fn init(memory: M) -> Self { + BaseVec::::init(memory, MAGIC) + .map(Self) + .expect("Failed to initialize a vector") } /// Returns the underlying memory instance. @@ -72,8 +76,10 @@ impl Vec { /// Adds a new item at the end of the vector. /// /// Complexity: O(max_size(T)) - pub fn push(&self, item: &T) -> Result<(), GrowFailed> { - self.0.push(item) + pub fn push(&self, item: &T) { + self.0 + .push(item) + .expect("Failed to push item to the vector"); } /// Removes the item at the end of the vector. diff --git a/src/vec/tests.rs b/src/vec/tests.rs index 1c05a772..ef422889 100644 --- a/src/vec/tests.rs +++ b/src/vec/tests.rs @@ -1,7 +1,7 @@ -use super::{InitError, Vec as StableVec}; +use super::Vec as StableVec; use crate::storable::{Bound, Storable}; use crate::vec_mem::VectorMemory as M; -use crate::{GrowFailed, Memory}; +use crate::Memory; use proptest::collection::vec as pvec; use proptest::prelude::*; use std::borrow::Cow; @@ -72,12 +72,12 @@ proptest! { #[test] fn init_after_new(vals in pvec(any::(), 0..20)) { - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); for v in vals { - sv.push(&v).unwrap(); + sv.push(&v); } let vec = sv.to_vec(); - prop_assert_eq!(StableVec::::init(sv.into_memory()).unwrap().to_vec(), vec); + prop_assert_eq!(StableVec::::init(sv.into_memory()).to_vec(), vec); } } @@ -85,12 +85,12 @@ fn check_push_pop_model( ops: Vec>, ) -> Result<(), TestCaseError> { let mut v = Vec::new(); - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); for op in ops { match op { Operation::Push(x) => { - sv.push(&x).unwrap(); + sv.push(&x); v.push(x); prop_assert_eq!(&sv.to_vec(), &v); } @@ -104,80 +104,75 @@ fn check_push_pop_model( } #[test] -fn test_init_type_compatibility() { - let v = StableVec::::new(M::default()).unwrap(); - - assert_eq!( - StableVec::::init(v.into_memory()).unwrap_err(), - InitError::IncompatibleElementType - ); - - let v = StableVec::::new(M::default()).unwrap(); - assert_eq!( - StableVec::, M>::init(v.into_memory()).unwrap_err(), - InitError::IncompatibleElementType - ); +#[should_panic(expected = "IncompatibleElementType")] +fn test_failure_incompatible_element_type() { + let v = StableVec::::new(M::default()); + StableVec::::init(v.into_memory()); } #[test] -fn test_init_failures() { - struct EmptyMem; - impl Memory for EmptyMem { - fn size(&self) -> u64 { - 0 - } - fn grow(&self, _: u64) -> i64 { - -1 - } - fn read(&self, _: u64, _: &mut [u8]) { - panic!("out of bounds") - } - fn write(&self, _: u64, _: &[u8]) { - panic!("out of bounds") - } +#[should_panic(expected = "IncompatibleElementType")] +fn test_failure_incompatible_element_type_2() { + let v = StableVec::::new(M::default()); + StableVec::, M>::init(v.into_memory()); +} + +struct EmptyMem; + +impl Memory for EmptyMem { + fn size(&self) -> u64 { + 0 + } + fn grow(&self, _: u64) -> i64 { + -1 + } + fn read(&self, _: u64, _: &mut [u8]) { + panic!("out of bounds") + } + fn write(&self, _: u64, _: &[u8]) { + panic!("out of bounds") } +} - assert_eq!( - StableVec::::new(EmptyMem).unwrap_err(), - GrowFailed { - current_size: 0, - delta: 1 - } - ); +#[test] +#[should_panic(expected = "GrowFailed { current_size: 0, delta: 1 }")] +fn test_failure_new() { + StableVec::::new(EmptyMem); +} - assert_eq!( - StableVec::::init(EmptyMem).unwrap_err(), - InitError::OutOfMemory, - ); +#[test] +#[should_panic(expected = "OutOfMemory")] +fn test_failure_init() { + StableVec::::init(EmptyMem); +} +#[test] +#[should_panic(expected = "BadMagic { actual: [83, 73, 67], expected: [83, 86, 67] }")] +fn test_failure_bad_magic() { + // InitError::BadMagic { actual: *b"SIC", expected: *b"SVC" } let mem = M::default(); mem.grow(1); mem.write(0, b"SIC\x01\x08\x00\x00\x00\x00\x00\x00\x00\x01"); - assert_eq!( - StableVec::::init(mem).unwrap_err(), - InitError::BadMagic { - actual: *b"SIC", - expected: *b"SVC" - }, - ); + StableVec::::init(mem); +} +#[test] +#[should_panic(expected = "IncompatibleVersion(15)")] +fn test_failure_incompatible_version() { let mem = M::default(); mem.grow(1); mem.write(0, b"SVC\x0f\x08\x00\x00\x00\x00\x00\x00\x00\x01"); - assert_eq!( - StableVec::::init(mem).unwrap_err(), - InitError::IncompatibleVersion(15), - ); + StableVec::::init(mem); } #[allow(clippy::iter_nth_zero)] #[test] fn test_iter() { - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); assert_eq!(sv.iter().next(), None); - sv.push(&1).unwrap(); - sv.push(&2).unwrap(); - sv.push(&3).unwrap(); + sv.push(&1); + sv.push(&2); + sv.push(&3); let mut iter = sv.iter(); assert_eq!(iter.size_hint(), (3, None)); @@ -235,11 +230,11 @@ fn test_iter() { #[test] fn test_iter_count() { - let sv = StableVec::::new(M::default()).unwrap(); - sv.push(&1).unwrap(); - sv.push(&2).unwrap(); - sv.push(&3).unwrap(); - sv.push(&4).unwrap(); + let sv = StableVec::::new(M::default()); + sv.push(&1); + sv.push(&2); + sv.push(&3); + sv.push(&4); { let mut iter = sv.iter(); iter.next_back(); @@ -280,17 +275,17 @@ impl crate::Storable for BuggyStruct { #[test] #[should_panic(expected = "expected an element with length <= 1 bytes, but found 4")] fn push_element_bigger_than_max_size_panics() { - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); // Insert a struct where the serialized size is > `MAX_SIZE`. Should panic. - sv.push(&BuggyStruct(vec![1, 2, 3, 4])).unwrap(); + sv.push(&BuggyStruct(vec![1, 2, 3, 4])); } #[test] #[should_panic(expected = "expected an element with length <= 1 bytes, but found 5")] fn set_element_bigger_than_max_size_panics() { - let sv = StableVec::::new(M::default()).unwrap(); + let sv = StableVec::::new(M::default()); // Insert a struct where the serialized size is <= `MAX_SIZE`. This should succeed. - sv.push(&BuggyStruct(vec![1])).unwrap(); + sv.push(&BuggyStruct(vec![1])); // Insert a struct where the serialized size is > `MAX_SIZE`. Should panic. sv.set(0, &BuggyStruct(vec![1, 2, 3, 4, 5])); @@ -299,10 +294,10 @@ fn set_element_bigger_than_max_size_panics() { #[test] fn set_last_element_to_large_blob() { use crate::storable::Blob; - let sv = StableVec::, M>::new(M::default()).unwrap(); + let sv = StableVec::, M>::new(M::default()); // Store a small blob. - sv.push(&Blob::default()).unwrap(); + sv.push(&Blob::default()); // Store a large blob that would require growing the memory. sv.set(0, &Blob::try_from(vec![1; 65536].as_slice()).unwrap()); diff --git a/tests/api_confomrance.rs b/tests/api_confomrance.rs index 7b5cb568..cbe9dd58 100644 --- a/tests/api_confomrance.rs +++ b/tests/api_confomrance.rs @@ -1,12 +1,15 @@ -use ic_stable_structures::btreemap::BTreeMap; -use ic_stable_structures::btreeset::BTreeSet; -use ic_stable_structures::min_heap::MinHeap; -use ic_stable_structures::vec::Vec as StableVec; use std::cell::RefCell; use std::cmp::Reverse; use std::collections::BinaryHeap; use std::rc::Rc; +use ic_stable_structures::btreemap::BTreeMap; +use ic_stable_structures::btreeset::BTreeSet; +use ic_stable_structures::cell::Cell as StableCell; +use ic_stable_structures::log::Log as StableLog; +use ic_stable_structures::min_heap::MinHeap; +use ic_stable_structures::vec::Vec as StableVec; + pub fn make_memory() -> Rc>> { Rc::new(RefCell::new(Vec::new())) } @@ -240,13 +243,13 @@ fn api_conformance_btreeset() { #[test] fn api_conformance_min_heap() { let mem = make_memory(); - let mut stable = MinHeap::new(mem).unwrap(); + let mut stable = MinHeap::new(mem); let mut std = BinaryHeap::new(); let n = 10_u32; // Push elements. for i in 0..n { - stable.push(&i).expect("push failed"); + stable.push(&i); std.push(Reverse(i)); } @@ -279,13 +282,13 @@ fn api_conformance_min_heap() { #[test] fn api_conformance_vec() { let mem = make_memory(); - let stable = StableVec::new(mem).unwrap(); + let stable = StableVec::new(mem); let mut std = Vec::new(); let n = 10_u32; // Push elements. for i in 0..n { - stable.push(&i).expect("push failed"); + stable.push(&i); std.push(i); } @@ -323,3 +326,60 @@ fn api_conformance_vec() { assert_eq!(stable.len(), std.len() as u64); assert_eq!(stable.is_empty(), std.is_empty()); } + +#[test] +fn api_conformance_cell() { + let mem = make_memory(); + + // use u32 for simplicity; also supported by Storable + let initial = 42u32; + let updated = 777u32; + + let mut stable = StableCell::new(mem.clone(), initial); + let std = RefCell::new(initial); + + // Get + assert_eq!(*stable.get(), *std.borrow()); + + // Set + let old_stable = stable.set(updated); + let old_std = std.replace(updated); + assert_eq!(old_stable, old_std); + + // After set + assert_eq!(*stable.get(), *std.borrow()); + + // Check that the value persists across re-init + let stable = StableCell::init(mem, 0); + assert_eq!(*stable.get(), updated); +} + +#[test] +fn api_conformance_log() { + let index_mem = make_memory(); + let data_mem = make_memory(); + let log = StableLog::new(index_mem.clone(), data_mem.clone()); + let mut std = Vec::new(); + + let n = 10_u32; + + // Append elements and compare returned indices + for i in 0..n { + let idx = log.append(&i).expect("append should succeed"); + assert_eq!(idx, std.len() as u64); + std.push(i); + } + + // Length and is_empty + assert_eq!(log.len(), std.len() as u64); + assert_eq!(log.is_empty(), std.is_empty()); + + // Get by index + for i in 0..n { + assert_eq!(log.get(i as u64), Some(std[i as usize])); + } + + // Iteration + let log_items: Vec<_> = log.iter().collect(); + assert_eq!(log_items, std); +}