blob: 6693fd071f8ce9973bb973b9a5018c51a69438ec [file] [log] [blame]
mod all {
use gix_fs::dir::create;
#[test]
fn a_deeply_nested_directory() -> crate::Result {
let dir = tempfile::tempdir()?;
let target = &dir.path().join("1").join("2").join("3").join("4").join("5").join("6");
let dir = create::all(target, Default::default())?;
assert_eq!(dir, target, "all subdirectories can be created");
Ok(())
}
}
mod iter {
pub use std::io::ErrorKind::*;
use gix_fs::dir::{
create,
create::{Error::*, Retries},
};
#[test]
fn an_existing_directory_causes_immediate_success() -> crate::Result {
let dir = tempfile::tempdir()?;
let mut it = create::Iter::new(dir.path());
assert_eq!(
it.next().expect("item").expect("success"),
dir.path(),
"first iteration is immediately successful"
);
assert!(it.next().is_none(), "iterator exhausted afterwards");
Ok(())
}
#[test]
fn a_single_directory_can_be_created_too() -> crate::Result {
let dir = tempfile::tempdir()?;
let new_dir = dir.path().join("new");
let mut it = create::Iter::new(&new_dir);
assert_eq!(
it.next().expect("item").expect("success"),
&new_dir,
"first iteration is immediately successful"
);
assert!(it.next().is_none(), "iterator exhausted afterwards");
assert!(new_dir.is_dir(), "the directory exists");
Ok(())
}
#[test]
fn multiple_intermediate_directories_are_created_automatically() -> crate::Result {
let dir = tempfile::tempdir()?;
let new_dir = dir.path().join("s1").join("s2").join("new");
let mut it = create::Iter::new(&new_dir);
assert!(
matches!(it.next(), Some(Err(Intermediate{dir, kind: k})) if k == NotFound && dir == new_dir),
"dir is not present"
);
assert!(
matches!(it.next(), Some(Err(Intermediate{dir, kind:k})) if k == NotFound && dir == new_dir.parent().unwrap()),
"parent dir is not present"
);
assert_eq!(
it.next().expect("item").expect("success"),
new_dir.parent().unwrap().parent().unwrap(),
"first subdir is created"
);
assert_eq!(
it.next().expect("item").expect("success"),
new_dir.parent().unwrap(),
"second subdir is created"
);
assert_eq!(
it.next().expect("item").expect("success"),
new_dir,
"target directory is created"
);
assert!(it.next().is_none(), "iterator depleted");
assert!(new_dir.is_dir(), "the directory exists");
Ok(())
}
#[test]
fn multiple_intermediate_directories_are_created_up_to_retries_limit() -> crate::Result {
let dir = tempfile::tempdir()?;
let new_dir = dir.path().join("s1").join("s2").join("new");
let mut it = create::Iter::new_with_retries(
&new_dir,
Retries {
on_create_directory_failure: 1,
..Default::default()
},
);
assert!(
matches!(it.next(), Some(Err(Permanent{ retries_left, dir, err, ..})) if retries_left.on_create_directory_failure == 0
&& err.kind() == NotFound
&& dir == new_dir),
"parent dir is not present and we run out of attempts"
);
assert!(it.next().is_none(), "iterator depleted");
assert!(!new_dir.is_dir(), "the wasn't created");
Ok(())
}
#[test]
fn an_existing_file_makes_directory_creation_fail_permanently() -> crate::Result {
let dir = tempfile::tempdir()?;
let new_dir = dir.path().join("also-file");
std::fs::write(&new_dir, [42])?;
assert!(new_dir.is_file());
let mut it = create::Iter::new(&new_dir);
assert!(
matches!(it.next(), Some(Err(Permanent{ dir, err, .. })) if err.kind() == AlreadyExists
&& dir == new_dir),
"parent dir is not present and we run out of attempts"
);
assert!(it.next().is_none(), "iterator depleted");
assert!(new_dir.is_file(), "file is untouched");
Ok(())
}
#[test]
fn racy_directory_creation_with_new_directory_being_deleted_not_enough_retries() -> crate::Result {
let dir = tempfile::tempdir()?;
let new_dir = dir.path().join("a").join("new");
let parent_dir = new_dir.parent().unwrap();
let mut it = create::Iter::new_with_retries(
&new_dir,
Retries {
to_create_entire_directory: 2,
on_create_directory_failure: 2,
..Default::default()
},
);
assert!(
matches!(it.nth(1), Some(Ok(dir)) if dir == parent_dir),
"parent dir is created"
);
// Someone deletes the new directory
std::fs::remove_dir(parent_dir)?;
assert!(
matches!(it.nth(1), Some(Ok(dir)) if dir == parent_dir),
"parent dir is created"
);
// Someone deletes the new directory, again
std::fs::remove_dir(parent_dir)?;
assert!(
matches!(it.next(), Some(Err(Permanent{ retries_left, dir, err, .. })) if retries_left.to_create_entire_directory == 0
&& retries_left.on_create_directory_failure == 1
&& err.kind() == NotFound
&& dir == new_dir),
"we run out of attempts to retry to combat against raciness"
);
Ok(())
}
#[test]
fn racy_directory_creation_with_new_directory_being_deleted() -> crate::Result {
let dir = tempfile::tempdir()?;
let new_dir = dir.path().join("a").join("new");
let parent_dir = new_dir.parent().unwrap();
let mut it = create::Iter::new(&new_dir);
assert!(
matches!(it.next(), Some(Err(Intermediate{dir, kind:k})) if k == NotFound && dir == new_dir),
"dir is not present, and we go up a level"
);
assert!(
matches!(it.next(), Some(Ok(dir)) if dir == parent_dir),
"parent dir is created"
);
// Someone deletes the new directory
std::fs::remove_dir(parent_dir)?;
assert!(
matches!(it.next(), Some(Err(Intermediate{dir, kind:k})) if k == NotFound && dir == new_dir),
"now when it tries the actual dir its not found"
);
assert!(
matches!(it.next(), Some(Ok(dir)) if dir == parent_dir),
"parent dir is created as it retries"
);
assert!(
matches!(it.next(), Some(Ok(dir)) if dir == new_dir),
"target dir is created successfully"
);
assert!(it.next().is_none(), "iterator depleted");
assert!(new_dir.is_dir());
Ok(())
}
}