summaryrefslogtreecommitdiffstats
path: root/vendor/gix-fs/tests/dir
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 02:49:50 +0000
commit9835e2ae736235810b4ea1c162ca5e65c547e770 (patch)
tree3fcebf40ed70e581d776a8a4c65923e8ec20e026 /vendor/gix-fs/tests/dir
parentReleasing progress-linux version 1.70.0+dfsg2-1~progress7.99u1. (diff)
downloadrustc-9835e2ae736235810b4ea1c162ca5e65c547e770.tar.xz
rustc-9835e2ae736235810b4ea1c162ca5e65c547e770.zip
Merging upstream version 1.71.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-fs/tests/dir')
-rw-r--r--vendor/gix-fs/tests/dir/create.rs194
-rw-r--r--vendor/gix-fs/tests/dir/mod.rs2
-rw-r--r--vendor/gix-fs/tests/dir/remove.rs146
3 files changed, 342 insertions, 0 deletions
diff --git a/vendor/gix-fs/tests/dir/create.rs b/vendor/gix-fs/tests/dir/create.rs
new file mode 100644
index 000000000..6693fd071
--- /dev/null
+++ b/vendor/gix-fs/tests/dir/create.rs
@@ -0,0 +1,194 @@
+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(())
+ }
+}
diff --git a/vendor/gix-fs/tests/dir/mod.rs b/vendor/gix-fs/tests/dir/mod.rs
new file mode 100644
index 000000000..0008e7ee8
--- /dev/null
+++ b/vendor/gix-fs/tests/dir/mod.rs
@@ -0,0 +1,2 @@
+mod create;
+mod remove;
diff --git a/vendor/gix-fs/tests/dir/remove.rs b/vendor/gix-fs/tests/dir/remove.rs
new file mode 100644
index 000000000..4b08e5147
--- /dev/null
+++ b/vendor/gix-fs/tests/dir/remove.rs
@@ -0,0 +1,146 @@
+mod empty_upwards_until_boundary {
+ use std::{io, path::Path};
+
+ use gix_fs::dir::remove;
+
+ #[test]
+ fn boundary_must_contain_target_dir() -> crate::Result {
+ let dir = tempfile::tempdir()?;
+ let (target, boundary) = (dir.path().join("a"), dir.path().join("b"));
+ std::fs::create_dir(&target)?;
+ std::fs::create_dir(&boundary)?;
+ assert!(matches!(remove::empty_upward_until_boundary(&target, &boundary),
+ Err(err) if err.kind() == io::ErrorKind::InvalidInput));
+ assert!(target.is_dir());
+ assert!(boundary.is_dir());
+ Ok(())
+ }
+ #[test]
+ fn target_directory_non_existing_causes_existing_parents_not_to_be_deleted() -> crate::Result {
+ let dir = tempfile::tempdir()?;
+ let parent = dir.path().join("a");
+ std::fs::create_dir(&parent)?;
+ let target = parent.join("not-existing");
+ assert_eq!(remove::empty_upward_until_boundary(&target, dir.path())?, target);
+ assert!(
+ parent.is_dir(),
+ "the parent wasn't touched if the target already wasn't present"
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn target_directory_being_a_file_immediately_fails() -> crate::Result {
+ let dir = tempfile::tempdir()?;
+ let target = dir.path().join("actually-a-file");
+ std::fs::write(&target, [42])?;
+ assert!(remove::empty_upward_until_boundary(&target, dir.path()).is_err()); // TODO: check for IsNotADirectory when it becomes stable
+ assert!(target.is_file(), "it didn't touch the file");
+ assert!(dir.path().is_dir(), "it won't touch the boundary");
+ Ok(())
+ }
+ #[test]
+ fn boundary_being_the_target_dir_always_succeeds_and_we_do_nothing() -> crate::Result {
+ let dir = tempfile::tempdir()?;
+ assert_eq!(remove::empty_upward_until_boundary(dir.path(), dir.path())?, dir.path());
+ assert!(dir.path().is_dir(), "it won't touch the boundary");
+ Ok(())
+ }
+ #[test]
+ fn a_directory_which_doesnt_exist_to_start_with_is_ok() -> crate::Result {
+ let dir = tempfile::tempdir()?;
+ let target = dir.path().join("does-not-exist");
+ assert_eq!(remove::empty_upward_until_boundary(&target, dir.path())?, target);
+ assert!(dir.path().is_dir(), "it won't touch the boundary");
+ Ok(())
+ }
+ #[test]
+ fn boundary_directory_doesnt_have_to_exist_either_if_the_target_doesnt() -> crate::Result {
+ let boundary = Path::new("/boundary");
+ let target = Path::new("/boundary/target");
+ assert_eq!(remove::empty_upward_until_boundary(target, boundary)?, target);
+ Ok(())
+ }
+ #[test]
+ fn nested_directory_deletion_works() -> crate::Result {
+ let dir = tempfile::tempdir()?;
+ let nested = dir.path().join("a").join("b").join("to-delete");
+ std::fs::create_dir_all(&nested)?;
+ assert_eq!(remove::empty_upward_until_boundary(&nested, dir.path())?, nested);
+ assert!(!nested.is_dir(), "it actually deleted the nested directory");
+ assert!(!nested.parent().unwrap().is_dir(), "parent one was deleted");
+ assert!(
+ !nested.parent().unwrap().parent().unwrap().is_dir(),
+ "parent two was deleted"
+ );
+ assert!(dir.path().is_dir(), "it won't touch the boundary");
+ Ok(())
+ }
+}
+
+mod empty_depth_first {
+ use std::{
+ fs::{create_dir, create_dir_all},
+ path::Path,
+ };
+
+ #[test]
+ fn non_empty_anywhere_and_deletion_fails() -> crate::Result {
+ let dir = tempfile::TempDir::new()?;
+ let touch = |base: &Path, name: &str| create_dir_all(base).and_then(|_| std::fs::write(base.join(name), b""));
+
+ let nested_parent = dir.path().join("a");
+ touch(&nested_parent, "hello.ext")?;
+
+ let tree_parent = dir.path().join("tree");
+ touch(&tree_parent.join("a").join("b"), "hello.ext")?;
+ create_dir_all(tree_parent.join("one").join("two").join("empty"))?;
+
+ assert!(gix_fs::dir::remove::empty_depth_first(nested_parent).is_err());
+ Ok(())
+ }
+
+ #[test]
+ fn nested_empty_and_single_empty_delete_successfully() {
+ let dir = tempfile::TempDir::new().unwrap();
+ let nested_parent = dir.path().join("a");
+ let nested = nested_parent.join("b").join("leaf");
+ create_dir_all(nested).unwrap();
+
+ let single_parent = dir.path().join("single");
+ create_dir(&single_parent).unwrap();
+
+ let tree_parent = dir.path().join("tree");
+ create_dir_all(tree_parent.join("a").join("b")).unwrap();
+ create_dir_all(tree_parent.join("one").join("two").join("three")).unwrap();
+ create_dir_all(tree_parent.join("c")).unwrap();
+ for empty in &[nested_parent, single_parent, tree_parent] {
+ gix_fs::dir::remove::empty_depth_first(empty).unwrap();
+ }
+ }
+}
+
+/// We assume that all checks above also apply to the iterator, so won't repeat them here
+/// Test outside interference only
+mod iter {
+ use gix_fs::dir::remove;
+
+ #[test]
+ fn racy_directory_creation_during_deletion_always_wins_immediately() -> crate::Result {
+ let dir = tempfile::tempdir()?;
+ let nested = dir.path().join("a").join("b").join("to-delete");
+ std::fs::create_dir_all(&nested)?;
+
+ let mut it = remove::Iter::new(&nested, dir.path())?;
+ assert_eq!(it.next().expect("item")?, nested, "delete leaves directory");
+
+ // recreate the deleted directory in racy fashion, causing the next-to-delete directory not to be empty.
+ std::fs::create_dir(&nested)?;
+ assert!(
+ it.next().expect("err item").is_err(),
+ "cannot delete non-empty directory" // TODO: check for IsADirectory when it becomes stable
+ );
+ assert!(it.next().is_none(), "iterator is depleted");
+ Ok(())
+ }
+}