Skip to content

Commit a6710c5

Browse files
committed
add tests for actual worktree checkouts to assure validations kick in
1 parent bec648d commit a6710c5

File tree

4 files changed

+71
-56
lines changed

4 files changed

+71
-56
lines changed
Binary file not shown.

gix-worktree-state/tests/state/checkout.rs

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ fn assure_is_empty(dir: impl AsRef<Path>) -> std::io::Result<()> {
5151
fn submodules_are_instantiated_as_directories() -> crate::Result {
5252
let mut opts = opts_from_probe();
5353
opts.overwrite_existing = false;
54-
let (_source_tree, destination, _index, _outcome) = checkout_index_in_tmp_dir(opts.clone(), "make_mixed")?;
54+
let (_source_tree, destination, _index, _outcome) = checkout_index_in_tmp_dir(opts.clone(), "make_mixed", None)?;
5555

5656
for path in ["m1", "modules/m1"] {
5757
let sm = destination.path().join(path);
@@ -68,7 +68,7 @@ fn accidental_writes_through_symlinks_are_prevented_if_overwriting_is_forbidden(
6868
// without overwrite mode, everything is safe.
6969
opts.overwrite_existing = false;
7070
let (source_tree, destination, _index, outcome) =
71-
checkout_index_in_tmp_dir(opts.clone(), "make_dangerous_symlink").unwrap();
71+
checkout_index_in_tmp_dir(opts.clone(), "make_dangerous_symlink", None).unwrap();
7272

7373
let source_files = dir_structure(&source_tree);
7474
let worktree_files = dir_structure(&destination);
@@ -109,7 +109,7 @@ fn writes_through_symlinks_are_prevented_even_if_overwriting_is_allowed() {
109109
// with overwrite mode
110110
opts.overwrite_existing = true;
111111
let (source_tree, destination, _index, outcome) =
112-
checkout_index_in_tmp_dir(opts.clone(), "make_dangerous_symlink").unwrap();
112+
checkout_index_in_tmp_dir(opts.clone(), "make_dangerous_symlink", None).unwrap();
113113

114114
let source_files = dir_structure(&source_tree);
115115
let worktree_files = dir_structure(&destination);
@@ -144,8 +144,13 @@ fn delayed_driver_process() -> crate::Result {
144144
opts.filter_process_delay = gix_filter::driver::apply::Delay::Allow;
145145
opts.destination_is_initially_empty = false;
146146
setup_filter_pipeline(opts.filters.options_mut());
147-
let (_source, destination, _index, outcome) =
148-
checkout_index_in_tmp_dir_opts(opts, "make_mixed_without_submodules_and_symlinks", |_| true, |_| Ok(()))?;
147+
let (_source, destination, _index, outcome) = checkout_index_in_tmp_dir_opts(
148+
opts,
149+
"make_mixed_without_submodules_and_symlinks",
150+
None,
151+
|_| true,
152+
|_| Ok(()),
153+
)?;
149154
assert_eq!(outcome.collisions.len(), 0);
150155
assert_eq!(outcome.errors.len(), 0);
151156
assert_eq!(outcome.files_updated, 5);
@@ -189,6 +194,7 @@ fn overwriting_files_and_lone_directories_works() -> crate::Result {
189194
let (source, destination, _index, outcome) = checkout_index_in_tmp_dir_opts(
190195
opts.clone(),
191196
"make_mixed",
197+
None,
192198
|_| true,
193199
|d| {
194200
let empty = d.join("empty");
@@ -254,7 +260,7 @@ fn symlinks_become_files_if_disabled() -> crate::Result {
254260
let mut opts = opts_from_probe();
255261
opts.fs.symlink = false;
256262
let (source_tree, destination, _index, outcome) =
257-
checkout_index_in_tmp_dir(opts.clone(), "make_mixed_without_submodules")?;
263+
checkout_index_in_tmp_dir(opts.clone(), "make_mixed_without_submodules", None)?;
258264

259265
assert_equality(&source_tree, &destination, opts.fs.symlink)?;
260266
assert!(outcome.collisions.is_empty());
@@ -270,7 +276,7 @@ fn dangling_symlinks_can_be_created() -> crate::Result {
270276
}
271277

272278
let (_source_tree, destination, _index, outcome) =
273-
checkout_index_in_tmp_dir(opts.clone(), "make_dangling_symlink")?;
279+
checkout_index_in_tmp_dir(opts.clone(), "make_dangling_symlink", None)?;
274280
let worktree_files = dir_structure(&destination);
275281
let worktree_files_stripped = stripped_prefix(&destination, &worktree_files);
276282

@@ -291,7 +297,7 @@ fn allow_or_disallow_symlinks() -> crate::Result {
291297
for allowed in &[false, true] {
292298
opts.fs.symlink = *allowed;
293299
let (source_tree, destination, _index, outcome) =
294-
checkout_index_in_tmp_dir(opts.clone(), "make_mixed_without_submodules")?;
300+
checkout_index_in_tmp_dir(opts.clone(), "make_mixed_without_submodules", None)?;
295301

296302
assert_equality(&source_tree, &destination, opts.fs.symlink)?;
297303
assert!(outcome.collisions.is_empty());
@@ -307,6 +313,7 @@ fn keep_going_collects_results() {
307313
let (_source_tree, destination, _index, outcome) = checkout_index_in_tmp_dir_opts(
308314
opts,
309315
"make_mixed_without_submodules",
316+
None,
310317
|_id| {
311318
count
312319
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
@@ -369,7 +376,7 @@ fn no_case_related_collisions_on_case_sensitive_filesystem() {
369376
return;
370377
}
371378
let (source_tree, destination, index, outcome) =
372-
checkout_index_in_tmp_dir(opts.clone(), "make_ignorecase_collisions").unwrap();
379+
checkout_index_in_tmp_dir(opts.clone(), "make_ignorecase_collisions", None).unwrap();
373380

374381
assert!(outcome.collisions.is_empty());
375382
let num_files = assert_equality(&source_tree, &destination, opts.fs.symlink).unwrap();
@@ -384,6 +391,48 @@ fn no_case_related_collisions_on_case_sensitive_filesystem() {
384391
);
385392
}
386393

394+
#[test]
395+
fn safety_checks_dotdot_trees() {
396+
let mut opts = opts_from_probe();
397+
let err =
398+
checkout_index_in_tmp_dir(opts.clone(), "make_traverse_trees", Some("traverse_dotdot_trees")).unwrap_err();
399+
let expected_err_msg = "Input path \"../outside\" contains relative or absolute components";
400+
assert_eq!(err.source().expect("inner").to_string(), expected_err_msg);
401+
402+
opts.keep_going = true;
403+
let (_source_tree, _destination, _index, outcome) =
404+
checkout_index_in_tmp_dir(opts, "make_traverse_trees", Some("traverse_dotdot_trees"))
405+
.expect("keep-going checks out as much as possible");
406+
assert_eq!(outcome.errors.len(), 1, "one path could not be checked out");
407+
assert_eq!(
408+
outcome.errors[0].error.source().expect("inner").to_string(),
409+
expected_err_msg
410+
);
411+
}
412+
413+
#[test]
414+
fn safety_checks_dotgit_trees() {
415+
let opts = opts_from_probe();
416+
let err =
417+
checkout_index_in_tmp_dir(opts.clone(), "make_traverse_trees", Some("traverse_dotgit_trees")).unwrap_err();
418+
assert_eq!(
419+
err.source().expect("inner").to_string(),
420+
"The .git name may never be used"
421+
);
422+
}
423+
424+
#[test]
425+
fn safety_checks_dotgit_ntfs_stream() {
426+
let opts = opts_from_probe();
427+
let err =
428+
checkout_index_in_tmp_dir(opts.clone(), "make_traverse_trees", Some("traverse_dotgit_stream")).unwrap_err();
429+
assert_eq!(
430+
err.source().expect("inner").to_string(),
431+
"The .git name may never be used",
432+
"note how it is still discovered even though the path is `.git::$INDEX_ALLOCATION`"
433+
);
434+
}
435+
387436
#[test]
388437
fn collisions_are_detected_on_a_case_insensitive_filesystem_even_with_delayed_filters() {
389438
let mut opts = opts_from_probe();
@@ -394,7 +443,7 @@ fn collisions_are_detected_on_a_case_insensitive_filesystem_even_with_delayed_fi
394443
setup_filter_pipeline(opts.filters.options_mut());
395444
opts.filter_process_delay = gix_filter::driver::apply::Delay::Allow;
396445
let (source_tree, destination, _index, outcome) =
397-
checkout_index_in_tmp_dir(opts, "make_ignorecase_collisions").unwrap();
446+
checkout_index_in_tmp_dir(opts, "make_ignorecase_collisions", None).unwrap();
398447

399448
let source_files = dir_structure(&source_tree);
400449
assert_eq!(
@@ -506,17 +555,26 @@ pub fn dir_structure<P: AsRef<std::path::Path>>(path: P) -> Vec<std::path::PathB
506555
fn checkout_index_in_tmp_dir(
507556
opts: gix_worktree_state::checkout::Options,
508557
name: &str,
558+
subdir_name: Option<&str>,
509559
) -> crate::Result<(PathBuf, TempDir, gix_index::File, gix_worktree_state::checkout::Outcome)> {
510-
checkout_index_in_tmp_dir_opts(opts, name, |_d| true, |_| Ok(()))
560+
checkout_index_in_tmp_dir_opts(opts, name, subdir_name, |_d| true, |_| Ok(()))
511561
}
512562

513563
fn checkout_index_in_tmp_dir_opts(
514564
opts: gix_worktree_state::checkout::Options,
515-
name: &str,
565+
script_name: &str,
566+
subdir_name: Option<&str>,
516567
allow_return_object: impl FnMut(&gix_hash::oid) -> bool + Send + Clone,
517568
prep_dest: impl Fn(&Path) -> std::io::Result<()>,
518569
) -> crate::Result<(PathBuf, TempDir, gix_index::File, gix_worktree_state::checkout::Outcome)> {
519-
let source_tree = fixture_path(name);
570+
let source_tree = {
571+
let root = fixture_path(script_name);
572+
if let Some(name) = subdir_name {
573+
root.join(name)
574+
} else {
575+
root
576+
}
577+
};
520578
let git_dir = source_tree.join(".git");
521579
let mut index = gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, false, Default::default())?;
522580
let odb = gix_odb::at(git_dir.join("objects"))?.into_inner().into_arc()?;

gix-worktree/tests/fixtures/make_traverse_literal_separators.sh

Lines changed: 0 additions & 43 deletions
This file was deleted.

0 commit comments

Comments
 (0)