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(()) } }