diff --git a/libraries/persistent_store/src/fragment.rs b/libraries/persistent_store/src/fragment.rs index 73b6d29..9aec377 100644 --- a/libraries/persistent_store/src/fragment.rs +++ b/libraries/persistent_store/src/fragment.rs @@ -177,3 +177,168 @@ fn get_handles(store: &Store, keys: &impl Keys) -> StoreResult = (0..60).collect(); + assert_eq!(store.insert(0, &value[..52]), Ok(())); + assert_eq!(store.insert(1, &value[52..]), Ok(())); + assert_eq!(read(&store, &(0..4)), Ok(Some(value))); + } + + #[test] + fn read_range_first_chunk() { + let mut store = MINIMAL.new_store(); + let value: Vec<_> = (0..60).collect(); + assert_eq!(store.insert(0, &value[..52]), Ok(())); + assert_eq!(store.insert(1, &value[52..]), Ok(())); + assert_eq!( + read_range(&store, &(0..4), 0..10), + Ok(Some((0..10).collect())) + ); + assert_eq!( + read_range(&store, &(0..4), 10..20), + Ok(Some((10..20).collect())) + ); + assert_eq!( + read_range(&store, &(0..4), 40..52), + Ok(Some((40..52).collect())) + ); + } + + #[test] + fn read_range_second_chunk() { + let mut store = MINIMAL.new_store(); + let value: Vec<_> = (0..60).collect(); + assert_eq!(store.insert(0, &value[..52]), Ok(())); + assert_eq!(store.insert(1, &value[52..]), Ok(())); + assert_eq!(read_range(&store, &(0..4), 52..53), Ok(Some(vec![52]))); + assert_eq!(read_range(&store, &(0..4), 53..54), Ok(Some(vec![53]))); + assert_eq!(read_range(&store, &(0..4), 59..60), Ok(Some(vec![59]))); + } + + #[test] + fn read_range_both_chunks() { + let mut store = MINIMAL.new_store(); + let value: Vec<_> = (0..60).collect(); + assert_eq!(store.insert(0, &value[..52]), Ok(())); + assert_eq!(store.insert(1, &value[52..]), Ok(())); + assert_eq!( + read_range(&store, &(0..4), 40..60), + Ok(Some((40..60).collect())) + ); + assert_eq!( + read_range(&store, &(0..4), 0..60), + Ok(Some((0..60).collect())) + ); + } + + #[test] + fn read_range_outside() { + let mut store = MINIMAL.new_store(); + let value: Vec<_> = (0..60).collect(); + assert_eq!(store.insert(0, &value[..52]), Ok(())); + assert_eq!(store.insert(1, &value[52..]), Ok(())); + assert_eq!( + read_range(&store, &(0..4), 40..100), + Ok(Some((40..60).collect())) + ); + assert_eq!(read_range(&store, &(0..4), 60..100), Ok(Some(vec![]))); + } + + #[test] + fn write_single_chunk() { + let mut store = MINIMAL.new_store(); + let value = b"hello".to_vec(); + assert_eq!(write(&mut store, &(0..4), &value), Ok(())); + assert_eq!(store.find(0), Ok(Some(value))); + assert_eq!(store.find(1), Ok(None)); + assert_eq!(store.find(2), Ok(None)); + assert_eq!(store.find(3), Ok(None)); + } + + #[test] + fn write_multiple_chunks() { + let mut store = MINIMAL.new_store(); + let value: Vec<_> = (0..60).collect(); + assert_eq!(write(&mut store, &(0..4), &value), Ok(())); + assert_eq!(store.find(0), Ok(Some((0..52).collect()))); + assert_eq!(store.find(1), Ok(Some((52..60).collect()))); + assert_eq!(store.find(2), Ok(None)); + assert_eq!(store.find(3), Ok(None)); + } + + #[test] + fn overwrite_less_chunks() { + let mut store = MINIMAL.new_store(); + let value: Vec<_> = (0..60).collect(); + assert_eq!(store.insert(0, &value[..52]), Ok(())); + assert_eq!(store.insert(1, &value[52..]), Ok(())); + let value: Vec<_> = (42..69).collect(); + assert_eq!(write(&mut store, &(0..4), &value), Ok(())); + assert_eq!(store.find(0), Ok(Some((42..69).collect()))); + assert_eq!(store.find(1), Ok(None)); + assert_eq!(store.find(2), Ok(None)); + assert_eq!(store.find(3), Ok(None)); + } + + #[test] + fn overwrite_needed_chunks() { + let mut store = MINIMAL.new_store(); + let mut value: Vec<_> = (0..60).collect(); + assert_eq!(store.insert(0, &value[..52]), Ok(())); + assert_eq!(store.insert(1, &value[52..]), Ok(())); + // Current lifetime is 2 words of overhead (2 insert) and 60 bytes of data. + let mut lifetime = 2 + 60 / 4; + assert_eq!(store.lifetime().unwrap().used(), lifetime); + // Update the value. + value.extend(60..80); + assert_eq!(write(&mut store, &(0..4), &value), Ok(())); + // Added lifetime is 1 word of overhead (1 insert) and (80 - 52) bytes of data. + lifetime += 1 + (80 - 52) / 4; + assert_eq!(store.lifetime().unwrap().used(), lifetime); + } + + #[test] + fn delete_empty() { + let mut store = MINIMAL.new_store(); + assert_eq!(delete(&mut store, &(0..4)), Ok(())); + assert_eq!(store.find(0), Ok(None)); + assert_eq!(store.find(1), Ok(None)); + assert_eq!(store.find(2), Ok(None)); + assert_eq!(store.find(3), Ok(None)); + } + + #[test] + fn delete_chunks() { + let mut store = MINIMAL.new_store(); + let value: Vec<_> = (0..60).collect(); + assert_eq!(store.insert(0, &value[..52]), Ok(())); + assert_eq!(store.insert(1, &value[52..]), Ok(())); + assert_eq!(delete(&mut store, &(0..4)), Ok(())); + assert_eq!(store.find(0), Ok(None)); + assert_eq!(store.find(1), Ok(None)); + assert_eq!(store.find(2), Ok(None)); + assert_eq!(store.find(3), Ok(None)); + } +} diff --git a/libraries/persistent_store/src/lib.rs b/libraries/persistent_store/src/lib.rs index 41acbaf..a8adc2b 100644 --- a/libraries/persistent_store/src/lib.rs +++ b/libraries/persistent_store/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2020 Google LLC +// Copyright 2019-2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -359,6 +359,8 @@ pub mod fragment; mod model; mod storage; mod store; +#[cfg(test)] +mod test; #[cfg(feature = "std")] pub use self::buffer::{BufferCorruptFunction, BufferOptions, BufferStorage}; diff --git a/libraries/persistent_store/src/store.rs b/libraries/persistent_store/src/store.rs index 2630950..f19f463 100644 --- a/libraries/persistent_store/src/store.rs +++ b/libraries/persistent_store/src/store.rs @@ -1301,71 +1301,15 @@ fn is_write_needed(source: &[u8], target: &[u8]) -> StoreResult { #[cfg(test)] mod tests { use super::*; - use crate::BufferOptions; - - #[derive(Clone)] - struct Config { - word_size: usize, - page_size: usize, - num_pages: usize, - max_word_writes: usize, - max_page_erases: usize, - } - - impl Config { - fn new_driver(&self) -> StoreDriverOff { - let options = BufferOptions { - word_size: self.word_size, - page_size: self.page_size, - max_word_writes: self.max_word_writes, - max_page_erases: self.max_page_erases, - strict_mode: true, - }; - StoreDriverOff::new(options, self.num_pages) - } - } - - const MINIMAL: Config = Config { - word_size: 4, - page_size: 64, - num_pages: 5, - max_word_writes: 2, - max_page_erases: 9, - }; - - const NORDIC: Config = Config { - word_size: 4, - page_size: 0x1000, - num_pages: 20, - max_word_writes: 2, - max_page_erases: 10000, - }; - - const TITAN: Config = Config { - word_size: 4, - page_size: 0x800, - num_pages: 10, - max_word_writes: 2, - max_page_erases: 10000, - }; + use crate::test::MINIMAL; #[test] - fn nordic_capacity() { - let driver = NORDIC.new_driver().power_on().unwrap(); - assert_eq!(driver.model().capacity().total, 19123); - } - - #[test] - fn titan_capacity() { - let driver = TITAN.new_driver().power_on().unwrap(); - assert_eq!(driver.model().capacity().total, 4315); - } - - #[test] - fn minimal_virt_page_size() { - // Make sure a virtual page has 14 words. We use this property in the other tests below to - // know whether entries are spanning, starting, and ending pages. - assert_eq!(MINIMAL.new_driver().model().format().virt_page_size(), 14); + fn is_write_needed_ok() { + assert_eq!(is_write_needed(&[], &[]), Ok(false)); + assert_eq!(is_write_needed(&[0], &[0]), Ok(false)); + assert_eq!(is_write_needed(&[0], &[1]), Err(StoreError::InvalidStorage)); + assert_eq!(is_write_needed(&[1], &[0]), Ok(true)); + assert_eq!(is_write_needed(&[1], &[1]), Ok(false)); } #[test] diff --git a/libraries/persistent_store/tests/config.rs b/libraries/persistent_store/src/test.rs similarity index 60% rename from libraries/persistent_store/tests/config.rs rename to libraries/persistent_store/src/test.rs index 8812743..2d20574 100644 --- a/libraries/persistent_store/tests/config.rs +++ b/libraries/persistent_store/src/test.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use persistent_store::{BufferOptions, BufferStorage, Store, StoreDriverOff}; +use crate::{BufferOptions, BufferStorage, Store, StoreDriverOff}; #[derive(Clone)] pub struct Config { @@ -47,3 +47,38 @@ pub const MINIMAL: Config = Config { max_word_writes: 2, max_page_erases: 9, }; + +const NORDIC: Config = Config { + word_size: 4, + page_size: 0x1000, + num_pages: 20, + max_word_writes: 2, + max_page_erases: 10000, +}; + +const TITAN: Config = Config { + word_size: 4, + page_size: 0x800, + num_pages: 10, + max_word_writes: 2, + max_page_erases: 10000, +}; + +#[test] +fn nordic_capacity() { + let driver = NORDIC.new_driver().power_on().unwrap(); + assert_eq!(driver.model().capacity().total, 19123); +} + +#[test] +fn titan_capacity() { + let driver = TITAN.new_driver().power_on().unwrap(); + assert_eq!(driver.model().capacity().total, 4315); +} + +#[test] +fn minimal_virt_page_size() { + // Make sure a virtual page has 14 words. We use this property in the other tests below to + // know whether entries are spanning, starting, and ending pages. + assert_eq!(MINIMAL.new_driver().model().format().virt_page_size(), 14); +} diff --git a/libraries/persistent_store/tests/fragment.rs b/libraries/persistent_store/tests/fragment.rs deleted file mode 100644 index fd045bc..0000000 --- a/libraries/persistent_store/tests/fragment.rs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use persistent_store::fragment; - -mod config; - -#[test] -fn read_empty_entry() { - let store = config::MINIMAL.new_store(); - assert_eq!(fragment::read(&store, &(0..4)), Ok(None)); -} - -#[test] -fn read_single_chunk() { - let mut store = config::MINIMAL.new_store(); - let value = b"hello".to_vec(); - assert_eq!(store.insert(0, &value), Ok(())); - assert_eq!(fragment::read(&store, &(0..4)), Ok(Some(value))); -} - -#[test] -fn read_multiple_chunks() { - let mut store = config::MINIMAL.new_store(); - let value: Vec<_> = (0..60).collect(); - assert_eq!(store.insert(0, &value[..52]), Ok(())); - assert_eq!(store.insert(1, &value[52..]), Ok(())); - assert_eq!(fragment::read(&store, &(0..4)), Ok(Some(value))); -} - -#[test] -fn read_range_first_chunk() { - let mut store = config::MINIMAL.new_store(); - let value: Vec<_> = (0..60).collect(); - assert_eq!(store.insert(0, &value[..52]), Ok(())); - assert_eq!(store.insert(1, &value[52..]), Ok(())); - assert_eq!( - fragment::read_range(&store, &(0..4), 0..10), - Ok(Some((0..10).collect())) - ); - assert_eq!( - fragment::read_range(&store, &(0..4), 10..20), - Ok(Some((10..20).collect())) - ); - assert_eq!( - fragment::read_range(&store, &(0..4), 40..52), - Ok(Some((40..52).collect())) - ); -} - -#[test] -fn read_range_second_chunk() { - let mut store = config::MINIMAL.new_store(); - let value: Vec<_> = (0..60).collect(); - assert_eq!(store.insert(0, &value[..52]), Ok(())); - assert_eq!(store.insert(1, &value[52..]), Ok(())); - assert_eq!( - fragment::read_range(&store, &(0..4), 52..53), - Ok(Some(vec![52])) - ); - assert_eq!( - fragment::read_range(&store, &(0..4), 53..54), - Ok(Some(vec![53])) - ); - assert_eq!( - fragment::read_range(&store, &(0..4), 59..60), - Ok(Some(vec![59])) - ); -} - -#[test] -fn read_range_both_chunks() { - let mut store = config::MINIMAL.new_store(); - let value: Vec<_> = (0..60).collect(); - assert_eq!(store.insert(0, &value[..52]), Ok(())); - assert_eq!(store.insert(1, &value[52..]), Ok(())); - assert_eq!( - fragment::read_range(&store, &(0..4), 40..60), - Ok(Some((40..60).collect())) - ); - assert_eq!( - fragment::read_range(&store, &(0..4), 0..60), - Ok(Some((0..60).collect())) - ); -} - -#[test] -fn read_range_outside() { - let mut store = config::MINIMAL.new_store(); - let value: Vec<_> = (0..60).collect(); - assert_eq!(store.insert(0, &value[..52]), Ok(())); - assert_eq!(store.insert(1, &value[52..]), Ok(())); - assert_eq!( - fragment::read_range(&store, &(0..4), 40..100), - Ok(Some((40..60).collect())) - ); - assert_eq!( - fragment::read_range(&store, &(0..4), 60..100), - Ok(Some(vec![])) - ); -} - -#[test] -fn write_single_chunk() { - let mut store = config::MINIMAL.new_store(); - let value = b"hello".to_vec(); - assert_eq!(fragment::write(&mut store, &(0..4), &value), Ok(())); - assert_eq!(store.find(0), Ok(Some(value))); - assert_eq!(store.find(1), Ok(None)); - assert_eq!(store.find(2), Ok(None)); - assert_eq!(store.find(3), Ok(None)); -} - -#[test] -fn write_multiple_chunks() { - let mut store = config::MINIMAL.new_store(); - let value: Vec<_> = (0..60).collect(); - assert_eq!(fragment::write(&mut store, &(0..4), &value), Ok(())); - assert_eq!(store.find(0), Ok(Some((0..52).collect()))); - assert_eq!(store.find(1), Ok(Some((52..60).collect()))); - assert_eq!(store.find(2), Ok(None)); - assert_eq!(store.find(3), Ok(None)); -} - -#[test] -fn overwrite_less_chunks() { - let mut store = config::MINIMAL.new_store(); - let value: Vec<_> = (0..60).collect(); - assert_eq!(store.insert(0, &value[..52]), Ok(())); - assert_eq!(store.insert(1, &value[52..]), Ok(())); - let value: Vec<_> = (42..69).collect(); - assert_eq!(fragment::write(&mut store, &(0..4), &value), Ok(())); - assert_eq!(store.find(0), Ok(Some((42..69).collect()))); - assert_eq!(store.find(1), Ok(None)); - assert_eq!(store.find(2), Ok(None)); - assert_eq!(store.find(3), Ok(None)); -} - -#[test] -fn overwrite_needed_chunks() { - let mut store = config::MINIMAL.new_store(); - let mut value: Vec<_> = (0..60).collect(); - assert_eq!(store.insert(0, &value[..52]), Ok(())); - assert_eq!(store.insert(1, &value[52..]), Ok(())); - // Current lifetime is 2 words of overhead (2 insert) and 60 bytes of data. - let mut lifetime = 2 + 60 / 4; - assert_eq!(store.lifetime().unwrap().used(), lifetime); - // Update the value. - value.extend(60..80); - assert_eq!(fragment::write(&mut store, &(0..4), &value), Ok(())); - // Added lifetime is 1 word of overhead (1 insert) and (80 - 52) bytes of data. - lifetime += 1 + (80 - 52) / 4; - assert_eq!(store.lifetime().unwrap().used(), lifetime); -} - -#[test] -fn delete_empty() { - let mut store = config::MINIMAL.new_store(); - assert_eq!(fragment::delete(&mut store, &(0..4)), Ok(())); - assert_eq!(store.find(0), Ok(None)); - assert_eq!(store.find(1), Ok(None)); - assert_eq!(store.find(2), Ok(None)); - assert_eq!(store.find(3), Ok(None)); -} - -#[test] -fn delete_chunks() { - let mut store = config::MINIMAL.new_store(); - let value: Vec<_> = (0..60).collect(); - assert_eq!(store.insert(0, &value[..52]), Ok(())); - assert_eq!(store.insert(1, &value[52..]), Ok(())); - assert_eq!(fragment::delete(&mut store, &(0..4)), Ok(())); - assert_eq!(store.find(0), Ok(None)); - assert_eq!(store.find(1), Ok(None)); - assert_eq!(store.find(2), Ok(None)); - assert_eq!(store.find(3), Ok(None)); -}