From f2e54b671c316319e9584a43e512409b5109f05b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 3 Dec 2022 13:42:16 +0100 Subject: [PATCH 1/2] Properly supporte nested globs when there are conflicts --- src/glob.rs | 46 ++++++++++++++++++- tests/inputs-nested/a/file.txt | 1 + tests/inputs-nested/b/file.txt | 1 + ...ob__basic_globbing_nested@a__file.txt.snap | 7 +++ ...ob__basic_globbing_nested@b__file.txt.snap | 7 +++ tests/test_glob.rs | 8 ++++ 6 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 tests/inputs-nested/a/file.txt create mode 100644 tests/inputs-nested/b/file.txt create mode 100644 tests/snapshots/test_glob__basic_globbing_nested@a__file.txt.snap create mode 100644 tests/snapshots/test_glob__basic_globbing_nested@b__file.txt.snap diff --git a/src/glob.rs b/src/glob.rs index e9efa657..09b6cbf5 100644 --- a/src/glob.rs +++ b/src/glob.rs @@ -1,5 +1,5 @@ use std::env; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Mutex; use globset::{GlobBuilder, GlobMatcher}; @@ -55,6 +55,8 @@ pub fn glob_exec(base: &Path, pattern: &str, mut f: F) { fail_fast: std::env::var("INSTA_GLOB_FAIL_FAST").as_deref() == Ok("1"), }); + // step 1: collect all matching files + let mut matching_files = vec![]; for file in walker { let file = file.unwrap(); let path = file.path(); @@ -71,9 +73,26 @@ pub fn glob_exec(base: &Path, pattern: &str, mut f: F) { continue; } + matching_files.push(path.to_path_buf()); + } + + // step 2: sort, determine common prefix and run assertions + matching_files.sort(); + let common_prefix = find_common_prefix(&matching_files); + for path in &matching_files { settings.set_input_file(path); - settings.set_snapshot_suffix(path.file_name().unwrap().to_str().unwrap()); + // if there is a common prefix, use that stirp down the input file. That way we + // can ensure that a glob like inputs/*/*.txt with a/file.txt and b/file.txt + // does not create two identical snapshot suffixes. Instead of file.txt for both + // it would end up as a/file.txt and b/file.txt. + let snapshot_suffix = if let Some(prefix) = common_prefix { + path.strip_prefix(prefix).unwrap().as_os_str() + } else { + path.file_name().unwrap() + }; + + settings.set_snapshot_suffix(snapshot_suffix.to_str().unwrap()); settings.bind(|| { f(path); }); @@ -104,3 +123,26 @@ pub fn glob_exec(base: &Path, pattern: &str, mut f: F) { ); } } + +fn find_common_prefix(sorted_paths: &[PathBuf]) -> Option<&Path> { + let first = sorted_paths.first()?; + let last = sorted_paths.last()?; + let mut prefix_len = 0; + for (a, b) in first.components().zip(last.components()) { + if a == b { + prefix_len += 1; + } else { + break; + } + } + + if prefix_len == 0 { + None + } else { + let mut components = first.components(); + for _ in 0..first.components().count() - prefix_len { + components.next_back(); + } + Some(components.as_path()) + } +} diff --git a/tests/inputs-nested/a/file.txt b/tests/inputs-nested/a/file.txt new file mode 100644 index 00000000..0496cb62 --- /dev/null +++ b/tests/inputs-nested/a/file.txt @@ -0,0 +1 @@ +Hello A diff --git a/tests/inputs-nested/b/file.txt b/tests/inputs-nested/b/file.txt new file mode 100644 index 00000000..a3eee9ba --- /dev/null +++ b/tests/inputs-nested/b/file.txt @@ -0,0 +1 @@ +Hello B diff --git a/tests/snapshots/test_glob__basic_globbing_nested@a__file.txt.snap b/tests/snapshots/test_glob__basic_globbing_nested@a__file.txt.snap new file mode 100644 index 00000000..254b4c60 --- /dev/null +++ b/tests/snapshots/test_glob__basic_globbing_nested@a__file.txt.snap @@ -0,0 +1,7 @@ +--- +source: tests/test_glob.rs +expression: "&contents" +input_file: tests/inputs-nested/a/file.txt +--- +Hello A + diff --git a/tests/snapshots/test_glob__basic_globbing_nested@b__file.txt.snap b/tests/snapshots/test_glob__basic_globbing_nested@b__file.txt.snap new file mode 100644 index 00000000..e6c38641 --- /dev/null +++ b/tests/snapshots/test_glob__basic_globbing_nested@b__file.txt.snap @@ -0,0 +1,7 @@ +--- +source: tests/test_glob.rs +expression: "&contents" +input_file: tests/inputs-nested/b/file.txt +--- +Hello B + diff --git a/tests/test_glob.rs b/tests/test_glob.rs index 08c483f1..512601e5 100644 --- a/tests/test_glob.rs +++ b/tests/test_glob.rs @@ -8,6 +8,14 @@ fn test_basic_globbing() { }); } +#[test] +fn test_basic_globbing_nested() { + insta::glob!("inputs-nested/*/*.txt", |path| { + let contents = std::fs::read_to_string(path).unwrap(); + insta::assert_snapshot!(&contents); + }); +} + #[test] fn test_globs_follow_links() { insta::glob!("link-to-inputs/*.txt", |path| { From e6c1474da283b9d0fe67c4dcc9f29d819b7c5e2e Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 3 Dec 2022 13:48:56 +0100 Subject: [PATCH 2/2] Refactor longest common prefix --- src/glob.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/glob.rs b/src/glob.rs index 09b6cbf5..a8685d51 100644 --- a/src/glob.rs +++ b/src/glob.rs @@ -127,22 +127,19 @@ pub fn glob_exec(base: &Path, pattern: &str, mut f: F) { fn find_common_prefix(sorted_paths: &[PathBuf]) -> Option<&Path> { let first = sorted_paths.first()?; let last = sorted_paths.last()?; - let mut prefix_len = 0; - for (a, b) in first.components().zip(last.components()) { - if a == b { - prefix_len += 1; - } else { - break; - } - } + let prefix_len = first + .components() + .zip(last.components()) + .take_while(|(a, b)| a == b) + .count(); if prefix_len == 0 { None } else { - let mut components = first.components(); + let mut prefix = first.components(); for _ in 0..first.components().count() - prefix_len { - components.next_back(); + prefix.next_back(); } - Some(components.as_path()) + Some(prefix.as_path()) } }