Skip to content

Commit

Permalink
chore: Write tests for PublicDirectory::merge
Browse files Browse the repository at this point in the history
  • Loading branch information
matheus23 committed Apr 17, 2024
1 parent 4746114 commit 5333d41
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 0 deletions.
10 changes: 10 additions & 0 deletions wnfs/proptest-regressions/public/directory.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 4ca9259264a7fb240270a7f3a3c9f702cc5f3f7ec8f8add28e774546901bb064 # shrinks to input = _TestMergeDirectoryPreferredArgs { path: [] }
cc 52d317f93f0d815bfd054e6147b32492abc79b100274458f3fc266d1d9f40083 # shrinks to input = _TestMergeCommutativityArgs { ops0: [Write(["a"], "a"), Mkdir(["a"])], ops1: [] }
cc 5d512e34a6b76473ff418d6cc7730003875ae30727a3155b2abc13d5f8313b58 # shrinks to input = _TestMergeCommutativityArgs { fs0: FileSystem { files: {}, dirs: {} }, fs1: FileSystem { files: {["a"]: "a"}, dirs: {["a"]} } }
cc d4c4529fd972a2a6af4dcecd28a289d11451203600ae18e001dbdd42fe19e245 # shrinks to input = _TestMergeCommutativityArgs { fs0: FileSystem { files: {["b"]: "a", ["b", "a"]: "a"}, dirs: {} }, fs1: FileSystem { files: {}, dirs: {} } }
187 changes: 187 additions & 0 deletions wnfs/src/public/directory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,193 @@ mod tests {
}
}

#[cfg(test)]
mod proptests {
use super::*;
use proptest::{
collection::{btree_map, btree_set, vec},
prelude::*,
};
use test_strategy::proptest;
use wnfs_common::MemoryBlockStore;

#[derive(Debug, Clone)]
struct FileSystem {
files: BTreeMap<Vec<String>, String>,
dirs: BTreeSet<Vec<String>>,
}

fn file_system() -> impl Strategy<Value = FileSystem> {
(
btree_map(vec(simple_string(), 1..10), simple_string(), 0..40),
btree_set(vec(simple_string(), 1..10), 0..40),
)
.prop_map(|(mut files, dirs)| {
files = files
.into_iter()
.filter(|(file_path, _)| {
!dirs
.iter()
.any(|dir_path| !dir_path.starts_with(&file_path))
})
.collect();
FileSystem { files, dirs }
})
.prop_filter("file overwritten by directory", valid_fs)
}

fn simple_string() -> impl Strategy<Value = String> {
(0..6u32).prop_map(|c| char::from_u32('a' as u32 + c).unwrap().to_string())
}

fn valid_fs(fs: &FileSystem) -> bool {
fs.files.iter().all(|(file_path, _)| {
!fs.dirs
.iter()
.any(|dir_path| dir_path.starts_with(&file_path))
&& !fs
.files
.iter()
.any(|(other_path, _)| other_path.starts_with(&file_path))
})
}

async fn convert_fs(
fs: FileSystem,
time: DateTime<Utc>,
store: &impl BlockStore,
) -> Result<Arc<PublicDirectory>> {
let mut dir = PublicDirectory::new_rc(time);
let FileSystem { files, dirs } = fs;
for (path, content) in files.iter() {
dir.write(&path, content.clone().into_bytes(), time, store)
.await?;
}

for path in dirs.iter() {
dir.mkdir(&path, time, store).await?;
}

Ok(dir)
}

#[proptest]
fn test_merge_directory_preferred(#[strategy(vec(simple_string(), 1..10))] path: Vec<String>) {
async_std::task::block_on(async move {
let store = &MemoryBlockStore::new();
let time = Utc::now();

let root0 = &mut PublicDirectory::new_rc(time);
let root1 = &mut PublicDirectory::new_rc(time);

root0
.write(&path, b"Should be overwritten".into(), time, store)
.await
.unwrap();

root1.mkdir(&path, time, store).await.unwrap();

root0.merge(root1, time, store).await.unwrap();

let node = root0
.get_node(&path, store)
.await
.unwrap()
.expect("merged fs contains the node");

prop_assert!(node.is_dir());

Ok(())
})?;
}

#[proptest]
fn test_merge_commutativity(
#[strategy(file_system())] fs0: FileSystem,
#[strategy(file_system())] fs1: FileSystem,
) {
async_std::task::block_on(async move {
let store = &MemoryBlockStore::new();
let time = Utc::now();

let root0 = convert_fs(fs0, time, store).await.unwrap();
let root1 = convert_fs(fs1, time, store).await.unwrap();

let mut merge_one_way = Arc::clone(&root0);
merge_one_way.merge(&root1, time, store).await.unwrap();
let mut merge_other_way = Arc::clone(&root1);
merge_other_way.merge(&root0, time, store).await.unwrap();

let cid_one_way = merge_one_way.store(store).await.unwrap();
let cid_other_way = merge_other_way.store(store).await.unwrap();

prop_assert_eq!(cid_one_way, cid_other_way);

Ok(())
})?;
}

#[proptest]
fn test_merge_associativity(
#[strategy(file_system())] fs0: FileSystem,
#[strategy(file_system())] fs1: FileSystem,
#[strategy(file_system())] fs2: FileSystem,
) {
async_std::task::block_on(async move {
let store = &MemoryBlockStore::new();
let time = Utc::now();
let root0 = convert_fs(fs0, time, store).await.unwrap();
let root1 = convert_fs(fs1, time, store).await.unwrap();
let root2 = convert_fs(fs2, time, store).await.unwrap();

let mut merge_0_1_then_2 = Arc::clone(&root0);
merge_0_1_then_2.merge(&root1, time, store).await.unwrap();
merge_0_1_then_2.merge(&root2, time, store).await.unwrap();

let mut merge_1_2 = Arc::clone(&root1);
merge_1_2.merge(&root2, time, store).await.unwrap();
let mut merge_0_with_1_2 = Arc::clone(&root0);
merge_0_with_1_2
.merge(&merge_1_2, time, store)
.await
.unwrap();

let cid_one_way = merge_0_1_then_2.store(store).await.unwrap();
let cid_other_way = merge_0_with_1_2.store(store).await.unwrap();

prop_assert_eq!(cid_one_way, cid_other_way);

Ok(())
})?;
}

#[proptest]
fn test_merge_directories_preserved(
#[strategy(file_system())] fs0: FileSystem,
#[strategy(file_system())] fs1: FileSystem,
) {
async_std::task::block_on(async move {
let store = &MemoryBlockStore::new();
let time = Utc::now();

let mut all_dirs = fs0.dirs.clone();
all_dirs.extend(fs1.dirs.iter().cloned());

let mut root = convert_fs(fs0, time, store).await.unwrap();
let root1 = convert_fs(fs1, time, store).await.unwrap();

root.merge(&root1, time, store).await.unwrap();

for dir in all_dirs {
let exists = root.get_node(&dir, store).await.unwrap().is_some();
prop_assert!(exists);
}

Ok(())
})?;
}
}

#[cfg(test)]
mod snapshot_tests {
use super::*;
Expand Down

0 comments on commit 5333d41

Please sign in to comment.