From ae0156d2876871fa13b3ee6b41717767c3960cc2 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Mon, 25 Jan 2021 17:30:50 +0100 Subject: [PATCH] Factor test tools between store and fragment Those need the driver to deal with the fact that the store is stateful. Those tests can't be moved to the test suite because they use private functions. --- libraries/persistent_store/src/fragment.rs | 165 +++++++++++++++ libraries/persistent_store/src/lib.rs | 4 +- libraries/persistent_store/src/store.rs | 70 +------ .../{tests/config.rs => src/test.rs} | 37 +++- libraries/persistent_store/tests/fragment.rs | 188 ------------------ 5 files changed, 211 insertions(+), 253 deletions(-) rename libraries/persistent_store/{tests/config.rs => src/test.rs} (60%) delete mode 100644 libraries/persistent_store/tests/fragment.rs 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)); -}