summaryrefslogtreecommitdiffstats
path: root/vendor/gix-glob/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:41:41 +0000
commit10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87 (patch)
treebdffd5d80c26cf4a7a518281a204be1ace85b4c1 /vendor/gix-glob/tests
parentReleasing progress-linux version 1.70.0+dfsg1-9~progress7.99u1. (diff)
downloadrustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.tar.xz
rustc-10ee2acdd26a7f1298c6f6d6b7af9b469fe29b87.zip
Merging upstream version 1.70.0+dfsg2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/gix-glob/tests')
-rw-r--r--vendor/gix-glob/tests/fixtures/generated-archives/make_baseline.tar.xzbin0 -> 10428 bytes
-rw-r--r--vendor/gix-glob/tests/fixtures/make_baseline.sh158
-rw-r--r--vendor/gix-glob/tests/glob.rs3
-rw-r--r--vendor/gix-glob/tests/parse/mod.rs155
-rw-r--r--vendor/gix-glob/tests/pattern/matching.rs325
-rw-r--r--vendor/gix-glob/tests/pattern/mod.rs18
-rw-r--r--vendor/gix-glob/tests/wildmatch/mod.rs374
7 files changed, 1033 insertions, 0 deletions
diff --git a/vendor/gix-glob/tests/fixtures/generated-archives/make_baseline.tar.xz b/vendor/gix-glob/tests/fixtures/generated-archives/make_baseline.tar.xz
new file mode 100644
index 000000000..5fc9dfdf1
--- /dev/null
+++ b/vendor/gix-glob/tests/fixtures/generated-archives/make_baseline.tar.xz
Binary files differ
diff --git a/vendor/gix-glob/tests/fixtures/make_baseline.sh b/vendor/gix-glob/tests/fixtures/make_baseline.sh
new file mode 100644
index 000000000..5787ff64c
--- /dev/null
+++ b/vendor/gix-glob/tests/fixtures/make_baseline.sh
@@ -0,0 +1,158 @@
+#!/bin/bash
+set -eu -o pipefail
+
+git init -q
+git config core.autocrlf false
+git config core.ignorecase false
+
+while read -r pattern value; do
+ echo "$pattern" "$value"
+ echo "$pattern" > .gitignore
+ echo "$value" | git check-ignore -vn --stdin 2>&1 || :
+done <<EOF >git-baseline.nmatch
+/*foo bam/barfoo/baz/bam
+/*foo bar/bam/barfoo/baz/bam
+foo foobaz
+*/\' XXX/\'
+/*foo bar/foo
+/*foo bar/bazfoo
+foo*bar foo/baz/bar
+/*foo.txt hello/foo.txt
+bar/foo baz/bar/foo
+*hello.txt hello.txt-and-then-some
+*hello.txt goodbye.txt
+*some/path/to/hello.txt some/path/to/hello.txt-and-then-some
+*some/path/to/hello.txt some/other/path/to/hello.txt
+*some/path/to/hello.txt a/bigger/some/path/to/hello.txt
+abc?def abc/def
+a*b*c abcd
+abc*abc*abc abcabcabcabcabcabcabca
+a[0-9]b a_b
+a[!0-9]b a0b
+a[!0-9]b a9b
+[!-] -
+a[^0-9]b a0b
+a[^0-9]b a9b
+[^-] -
+{a,b} a
+{a,b} b
+{[}],foo} }
+{foo} foo
+{*.foo,*.bar,*.wat} test.foo
+{*.foo,*.bar,*.wat} test.bar
+{*.foo,*.bar,*.wat} test.wat
+abc*def abc/def
+aBcDeFg abcdefg
+aBcDeFg ABCDEFG
+aBcDeFg AbCdEfG
+some/**/needle.txt some/other/notthis.txt
+some/**/**/needle.txt some/other/notthis.txt
+/**/test one/notthis
+/**/test notthis
+**/.* ab.c
+**/.* abc/ab.c
+.*/** a.bc
+.*/** abc/a.bc
+./foo foo
+**/foo foofoo
+**/foo/bar foofoo/bar
+/*.c mozilla-sha1/sha1.c
+**/m4/ltoptions.m4 csharp/src/packages/repositories.config
+some/*/needle.txt some/needle.txt
+some/*/needle.txt some/one/two/needle.txt
+some/*/needle.txt some/one/two/three/needle.txt
+.*/** .abc
+foo/** foo
+{**/src/**,foo} abc/src/bar
+{**/src/**,foo} foo
+abc[/]def abc/def
+EOF
+
+while read -r pattern value; do
+ echo "$pattern" "$value"
+ echo "$pattern" > .gitignore
+ echo "$value" | git check-ignore -vn --stdin 2>&1 || :
+done <<EOF >git-baseline.match
+*/' XXX/'
+\a a
+\\\[a-z] \a
+\\\? \a
+\\\* \\
+/*foo.txt barfoo.txt
+*foo.txt bar/foo.txt
+*.c mozilla-sha1/sha1.c
+*.rs .rs
+*hello.txt hello.txt
+*hello.txt gareth_says_hello.txt
+*hello.txt some/path/to/hello.txt
+/*foo.txt foo.txt
+*hello.txt some\path\to\hello.txt
+*hello.txt an/absolute/path/to/hello.txt
+*some/path/to/hello.txt some/path/to/hello.txt
+a foo/a
+a a
+a*b a_b
+a*b*c abc
+a*b*c a_b_c
+a*b*c a___b___c
+abc*abc*abc abcabcabcabcabcabcabc
+a*a*a*a*a*a*a*a*a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+a*b[xyz]c*d abxcdbxcddd
+☃ ☃
+** abcde
+** .asdf
+** x/.asdf
+a[0-9]b a0b
+a[0-9]b a9b
+a[!0-9]b a_b
+[a-z123] 1
+[1a-z23] 1
+[123a-z] 1
+[abc-] -
+[-abc] -
+[-a-c] b
+[a-c-] b
+[-] -
+a[^0-9]b a_b
+_[[]_[]]_[?]_[*]_!_ _[_]_?_*_!_
+a,b a,b
+\[ [
+\? ?
+\* *
+aBcDeFg aBcDeFg
+some/**/needle.txt some/needle.txt
+some/**/needle.txt some/one/needle.txt
+some/**/needle.txt some/one/two/needle.txt
+some/**/needle.txt some/other/needle.txt
+some/**/**/needle.txt some/needle.txt
+some/**/**/needle.txt some/one/needle.txt
+some/**/**/needle.txt some/one/two/needle.txt
+some/**/**/needle.txt some/other/needle.txt
+**/test one/two/test
+**/test one/test
+**/test test
+/**/test one/two/test
+/**/test one/test
+/**/test test
+**/.* .abc
+**/.* abc/.abc
+**/foo/bar foo/bar
+.*/** .abc/abc
+test/** test/
+test/** test/one
+test/** test/one/two
+some/*/needle.txt some/one/needle.txt
+abc/def abc/def
+EOF
+
+git config core.ignorecase true
+while read -r pattern value; do
+ echo "$pattern" "$value"
+ echo "$pattern" > .gitignore
+ echo "$value" | git check-ignore -vn --stdin 2>&1 || :
+done <<EOF >git-baseline.match-icase
+aBcDeFg aBcDeFg
+aBcDeFg abcdefg
+aBcDeFg ABCDEFG
+aBcDeFg AbCdEfG
+EOF
diff --git a/vendor/gix-glob/tests/glob.rs b/vendor/gix-glob/tests/glob.rs
new file mode 100644
index 000000000..3a90f1d51
--- /dev/null
+++ b/vendor/gix-glob/tests/glob.rs
@@ -0,0 +1,3 @@
+mod parse;
+mod pattern;
+mod wildmatch;
diff --git a/vendor/gix-glob/tests/parse/mod.rs b/vendor/gix-glob/tests/parse/mod.rs
new file mode 100644
index 000000000..d6be0df24
--- /dev/null
+++ b/vendor/gix-glob/tests/parse/mod.rs
@@ -0,0 +1,155 @@
+use gix_glob::{pattern::Mode, Pattern};
+
+#[test]
+fn mark_ends_with_pattern_specifically() {
+ assert_eq!(
+ gix_glob::parse(br"*literal"),
+ pat(r"*literal", Mode::NO_SUB_DIR | Mode::ENDS_WITH, Some(0))
+ );
+ assert_eq!(
+ gix_glob::parse(br"**literal"),
+ pat(r"**literal", Mode::NO_SUB_DIR, Some(0)),
+ "double-asterisk won't allow for fast comparisons"
+ );
+ assert_eq!(
+ gix_glob::parse(br"*litera[l]"),
+ pat(r"*litera[l]", Mode::NO_SUB_DIR, Some(0))
+ );
+ assert_eq!(
+ gix_glob::parse(br"*litera?"),
+ pat(r"*litera?", Mode::NO_SUB_DIR, Some(0))
+ );
+ assert_eq!(
+ gix_glob::parse(br"*litera\?"),
+ pat(r"*litera\?", Mode::NO_SUB_DIR, Some(0)),
+ "for now we don't handle escapes properly like git seems to do"
+ );
+}
+
+fn pat(pattern: &str, mode: Mode, first_glob_char_pos: Option<usize>) -> Option<Pattern> {
+ Some(Pattern {
+ text: pattern.into(),
+ mode,
+ first_wildcard_pos: first_glob_char_pos,
+ })
+}
+
+#[test]
+fn whitespace_only_is_ignored() {
+ assert!(gix_glob::parse(b"\n\r\n\t\t \n").is_none());
+}
+
+#[test]
+fn hash_symbols_are_not_special() {
+ assert_eq!(
+ gix_glob::parse(b"# hello world"),
+ pat("# hello world", Mode::NO_SUB_DIR, None)
+ );
+}
+
+#[test]
+fn backslashes_before_hashes_are_considered_an_escape_sequence() {
+ assert_eq!(gix_glob::parse(br"\#hello"), pat(r"#hello", Mode::NO_SUB_DIR, None));
+}
+
+#[test]
+fn backslashes_are_part_of_the_pattern_if_not_in_specific_positions() {
+ assert_eq!(
+ gix_glob::parse(br"\hello\world"),
+ pat(r"\hello\world", Mode::NO_SUB_DIR, Some(0))
+ );
+}
+
+#[test]
+fn leading_exclamation_mark_negates_pattern() {
+ assert_eq!(
+ gix_glob::parse(b"!hello"),
+ pat("hello", Mode::NEGATIVE | Mode::NO_SUB_DIR, None)
+ );
+}
+
+#[test]
+fn leading_exclamation_marks_can_be_escaped_with_backslash() {
+ assert_eq!(gix_glob::parse(br"\!hello"), pat("!hello", Mode::NO_SUB_DIR, None));
+}
+
+#[test]
+fn leading_slashes_mark_patterns_as_absolute() {
+ assert_eq!(
+ gix_glob::parse(br"/absolute"),
+ pat("absolute", Mode::NO_SUB_DIR | Mode::ABSOLUTE, None)
+ );
+
+ assert_eq!(
+ gix_glob::parse(br"/absolute/path"),
+ pat("absolute/path", Mode::ABSOLUTE, None)
+ );
+}
+
+#[test]
+fn absence_of_sub_directories_are_marked() {
+ assert_eq!(gix_glob::parse(br"a/b"), pat("a/b", Mode::empty(), None));
+ assert_eq!(gix_glob::parse(br"ab"), pat("ab", Mode::NO_SUB_DIR, None));
+}
+
+#[test]
+fn trailing_slashes_are_marked_and_removed() {
+ assert_eq!(
+ gix_glob::parse(b"dir/"),
+ pat("dir", Mode::MUST_BE_DIR | Mode::NO_SUB_DIR, None)
+ );
+ assert_eq!(
+ gix_glob::parse(b"dir///"),
+ pat("dir//", Mode::MUST_BE_DIR, None),
+ "but only the last slash is removed"
+ );
+}
+
+#[test]
+fn trailing_spaces_are_ignored() {
+ assert_eq!(gix_glob::parse(br"a "), pat("a", Mode::NO_SUB_DIR, None));
+ assert_eq!(
+ gix_glob::parse(b"a\t\t "),
+ pat("a\t\t", Mode::NO_SUB_DIR, None),
+ "trailing tabs are not ignored"
+ );
+}
+
+#[test]
+fn trailing_spaces_can_be_escaped_to_be_literal() {
+ assert_eq!(
+ gix_glob::parse(br"a \ "),
+ pat("a ", Mode::NO_SUB_DIR, None),
+ "a single escape in front of the last desired space is enough"
+ );
+ assert_eq!(
+ gix_glob::parse(br"a b c "),
+ pat("a b c", Mode::NO_SUB_DIR, None),
+ "spaces in the middle are fine"
+ );
+ assert_eq!(
+ gix_glob::parse(br"a\ \ \ "),
+ pat("a ", Mode::NO_SUB_DIR, None),
+ "one can also escape every single one"
+ );
+ assert_eq!(
+ gix_glob::parse(br"a \ "),
+ pat("a ", Mode::NO_SUB_DIR, None),
+ "or just the one in the middle, losing the last actual space"
+ );
+ assert_eq!(
+ gix_glob::parse(br"a \"),
+ pat("a ", Mode::NO_SUB_DIR, None),
+ "escaping nothing also works as a whitespace protection"
+ );
+ assert_eq!(
+ gix_glob::parse(br"a \\\ "),
+ pat(r"a ", Mode::NO_SUB_DIR, None),
+ "strange things like these work too"
+ );
+ assert_eq!(
+ gix_glob::parse(br"a \\ "),
+ pat(r"a ", Mode::NO_SUB_DIR, None),
+ "strange things like these work as well"
+ );
+}
diff --git a/vendor/gix-glob/tests/pattern/matching.rs b/vendor/gix-glob/tests/pattern/matching.rs
new file mode 100644
index 000000000..3e757f8d6
--- /dev/null
+++ b/vendor/gix-glob/tests/pattern/matching.rs
@@ -0,0 +1,325 @@
+use std::collections::BTreeSet;
+
+use bstr::{BStr, ByteSlice};
+use gix_glob::{pattern, pattern::Case};
+
+#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
+pub struct GitMatch<'a> {
+ pattern: &'a BStr,
+ value: &'a BStr,
+ /// True if git could match `value` with `pattern`
+ is_match: bool,
+}
+
+pub struct Baseline<'a> {
+ inner: bstr::Lines<'a>,
+}
+
+impl<'a> Iterator for Baseline<'a> {
+ type Item = GitMatch<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut tokens = self.inner.next()?.splitn(2, |b| *b == b' ');
+ let pattern = tokens.next().expect("pattern").as_bstr();
+ let value = tokens.next().expect("value").as_bstr().trim_start().as_bstr();
+
+ let git_match = self.inner.next()?;
+ let is_match = !git_match.starts_with(b"::\t");
+ Some(GitMatch {
+ pattern,
+ value,
+ is_match,
+ })
+ }
+}
+
+impl<'a> Baseline<'a> {
+ fn new(input: &'a [u8]) -> Self {
+ Baseline {
+ inner: input.as_bstr().lines(),
+ }
+ }
+}
+
+#[test]
+fn compare_baseline_with_ours() {
+ let dir = gix_testtools::scripted_fixture_read_only("make_baseline.sh").unwrap();
+ let (mut total_matches, mut total_correct, mut panics) = (0, 0, 0);
+ let mut mismatches = Vec::new();
+ for (input_file, expected_matches, case) in &[
+ ("git-baseline.match", true, pattern::Case::Sensitive),
+ ("git-baseline.nmatch", false, pattern::Case::Sensitive),
+ ("git-baseline.match-icase", true, pattern::Case::Fold),
+ ] {
+ let input = std::fs::read(dir.join(*input_file)).unwrap();
+ let mut seen = BTreeSet::default();
+
+ for m @ GitMatch {
+ pattern,
+ value,
+ is_match,
+ } in Baseline::new(&input)
+ {
+ total_matches += 1;
+ assert!(seen.insert(m), "duplicate match entry: {m:?}");
+ assert_eq!(
+ is_match, *expected_matches,
+ "baseline for matches must be {expected_matches} - check baseline and git version: {m:?}"
+ );
+ match std::panic::catch_unwind(|| {
+ let pattern = pat(pattern);
+ pattern.matches_repo_relative_path(value, basename_start_pos(value), None, *case)
+ }) {
+ Ok(actual_match) => {
+ if actual_match == is_match {
+ total_correct += 1;
+ } else {
+ mismatches.push((pattern.to_owned(), value.to_owned(), is_match, expected_matches));
+ }
+ }
+ Err(_) => {
+ panics += 1;
+ continue;
+ }
+ };
+ }
+ }
+
+ dbg!(mismatches);
+ assert_eq!(
+ total_correct,
+ total_matches - panics,
+ "We perfectly agree with git here"
+ );
+ assert_eq!(panics, 0);
+}
+
+#[test]
+fn non_dirs_for_must_be_dir_patterns_are_ignored() {
+ let pattern = pat("hello/");
+
+ assert!(pattern.mode.contains(pattern::Mode::MUST_BE_DIR));
+ assert_eq!(
+ pattern.text, "hello",
+ "a dir pattern doesn't actually end with the trailing slash"
+ );
+ let path = "hello";
+ assert!(
+ !pattern.matches_repo_relative_path(path, None, false.into() /* is-dir */, Case::Sensitive),
+ "non-dirs never match a dir pattern"
+ );
+ assert!(
+ pattern.matches_repo_relative_path(path, None, true.into() /* is-dir */, Case::Sensitive),
+ "dirs can match a dir pattern with the normal rules"
+ );
+}
+
+#[test]
+fn matches_of_absolute_paths_work() {
+ let pattern = "/hello/git";
+ assert!(
+ gix_glob::wildmatch(pattern.into(), pattern.into(), gix_glob::wildmatch::Mode::empty()),
+ "patterns always match themselves"
+ );
+ assert!(
+ gix_glob::wildmatch(
+ pattern.into(),
+ pattern.into(),
+ gix_glob::wildmatch::Mode::NO_MATCH_SLASH_LITERAL
+ ),
+ "patterns always match themselves, path mode doesn't change that"
+ );
+}
+
+#[test]
+fn basename_matches_from_end() {
+ let pat = &pat("foo");
+ assert!(match_file(pat, "FoO", Case::Fold));
+ assert!(!match_file(pat, "FoOo", Case::Fold));
+ assert!(!match_file(pat, "Foo", Case::Sensitive));
+ assert!(match_file(pat, "foo", Case::Sensitive));
+ assert!(!match_file(pat, "Foo", Case::Sensitive));
+ assert!(!match_file(pat, "barfoo", Case::Sensitive));
+}
+
+#[test]
+fn absolute_basename_matches_only_from_beginning() {
+ let pat = &pat("/foo");
+ assert!(match_file(pat, "FoO", Case::Fold));
+ assert!(!match_file(pat, "bar/Foo", Case::Fold));
+ assert!(match_file(pat, "foo", Case::Sensitive));
+ assert!(!match_file(pat, "Foo", Case::Sensitive));
+ assert!(!match_file(pat, "bar/foo", Case::Sensitive));
+}
+
+#[test]
+fn absolute_path_matches_only_from_beginning() {
+ let pat = &pat("/bar/foo");
+ assert!(!match_file(pat, "FoO", Case::Fold));
+ assert!(match_file(pat, "bar/Foo", Case::Fold));
+ assert!(!match_file(pat, "foo", Case::Sensitive));
+ assert!(match_file(pat, "bar/foo", Case::Sensitive));
+ assert!(!match_file(pat, "bar/Foo", Case::Sensitive));
+}
+
+#[test]
+fn absolute_path_with_recursive_glob_detects_mismatches_quickly() {
+ let pat = &pat("/bar/foo/**");
+ assert!(!match_file(pat, "FoO", Case::Fold));
+ assert!(!match_file(pat, "bar/Fooo", Case::Fold));
+ assert!(!match_file(pat, "baz/bar/Foo", Case::Fold));
+}
+
+#[test]
+fn absolute_path_with_recursive_glob_can_do_case_insensitive_prefix_search() {
+ let pat = &pat("/bar/foo/**");
+ assert!(!match_file(pat, "bar/Foo/match", Case::Sensitive));
+ assert!(match_file(pat, "bar/Foo/match", Case::Fold));
+}
+
+#[test]
+fn relative_path_does_not_match_from_end() {
+ for pattern in &["bar/foo", "/bar/foo"] {
+ let pattern = &pat(*pattern);
+ assert!(!match_file(pattern, "FoO", Case::Fold));
+ assert!(match_file(pattern, "bar/Foo", Case::Fold));
+ assert!(!match_file(pattern, "baz/bar/Foo", Case::Fold));
+ assert!(!match_file(pattern, "foo", Case::Sensitive));
+ assert!(match_file(pattern, "bar/foo", Case::Sensitive));
+ assert!(!match_file(pattern, "baz/bar/foo", Case::Sensitive));
+ assert!(!match_file(pattern, "Baz/bar/Foo", Case::Sensitive));
+ }
+}
+
+#[test]
+fn basename_glob_and_literal_is_ends_with() {
+ let pattern = &pat("*foo");
+ assert!(match_file(pattern, "FoO", Case::Fold));
+ assert!(match_file(pattern, "BarFoO", Case::Fold));
+ assert!(!match_file(pattern, "BarFoOo", Case::Fold));
+ assert!(!match_file(pattern, "Foo", Case::Sensitive));
+ assert!(!match_file(pattern, "BarFoo", Case::Sensitive));
+ assert!(match_file(pattern, "barfoo", Case::Sensitive));
+ assert!(!match_file(pattern, "barfooo", Case::Sensitive));
+
+ assert!(match_file(pattern, "bar/foo", Case::Sensitive));
+ assert!(match_file(pattern, "bar/bazfoo", Case::Sensitive));
+}
+
+#[test]
+fn special_cases_from_corpus() {
+ let pattern = &pat("foo*bar");
+ assert!(
+ !match_file(pattern, "foo/baz/bar", Case::Sensitive),
+ "asterisk does not match path separators"
+ );
+ let pattern = &pat("*some/path/to/hello.txt");
+ assert!(
+ !match_file(pattern, "a/bigger/some/path/to/hello.txt", Case::Sensitive),
+ "asterisk doesn't match path separators"
+ );
+
+ let pattern = &pat("/*foo.txt");
+ assert!(match_file(pattern, "hello-foo.txt", Case::Sensitive));
+ assert!(
+ !match_file(pattern, "hello/foo.txt", Case::Sensitive),
+ "absolute single asterisk doesn't match paths"
+ );
+}
+
+#[test]
+fn absolute_basename_glob_and_literal_is_ends_with_in_basenames() {
+ let pattern = &pat("/*foo");
+
+ assert!(match_file(pattern, "FoO", Case::Fold));
+ assert!(match_file(pattern, "BarFoO", Case::Fold));
+ assert!(!match_file(pattern, "BarFoOo", Case::Fold));
+ assert!(!match_file(pattern, "Foo", Case::Sensitive));
+ assert!(!match_file(pattern, "BarFoo", Case::Sensitive));
+ assert!(match_file(pattern, "barfoo", Case::Sensitive));
+ assert!(!match_file(pattern, "barfooo", Case::Sensitive));
+}
+
+#[test]
+fn absolute_basename_glob_and_literal_is_glob_in_paths() {
+ let pattern = &pat("/*foo");
+
+ assert!(!match_file(pattern, "bar/foo", Case::Sensitive), "* does not match /");
+ assert!(!match_file(pattern, "bar/bazfoo", Case::Sensitive));
+}
+
+#[test]
+fn negated_patterns_are_handled_by_caller() {
+ let pattern = &pat("!foo");
+ assert!(
+ match_file(pattern, "foo", Case::Sensitive),
+ "negative patterns match like any other"
+ );
+ assert!(
+ pattern.is_negative(),
+ "the caller checks for the negative flag and acts accordingly"
+ );
+}
+#[test]
+fn names_do_not_automatically_match_entire_directories() {
+ // this feature is implemented with the directory stack.
+ let pattern = &pat("foo");
+ assert!(!match_file(pattern, "foobar", Case::Sensitive));
+ assert!(!match_file(pattern, "foo/bar", Case::Sensitive));
+ assert!(!match_file(pattern, "foo/bar/baz", Case::Sensitive));
+}
+
+#[test]
+fn directory_patterns_do_not_match_files_within_a_directory_as_well_like_slash_star_star() {
+ // this feature is implemented with the directory stack, which excludes entire directories
+ let pattern = &pat("dir/");
+ assert!(!match_path(pattern, "dir/file", None, Case::Sensitive));
+ assert!(!match_path(pattern, "base/dir/file", None, Case::Sensitive));
+ assert!(!match_path(pattern, "base/ndir/file", None, Case::Sensitive));
+ assert!(!match_path(pattern, "Dir/File", None, Case::Fold));
+ assert!(!match_path(pattern, "Base/Dir/File", None, Case::Fold));
+ assert!(!match_path(pattern, "dir2/file", None, Case::Sensitive));
+
+ let pattern = &pat("dir/sub-dir/");
+ assert!(!match_path(pattern, "dir/sub-dir/file", None, Case::Sensitive));
+ assert!(!match_path(pattern, "dir/Sub-dir/File", None, Case::Fold));
+ assert!(!match_path(pattern, "dir/Sub-dir2/File", None, Case::Fold));
+}
+
+#[test]
+fn single_paths_match_anywhere() {
+ let pattern = &pat("target");
+ assert!(match_file(pattern, "dir/target", Case::Sensitive));
+ assert!(!match_file(pattern, "dir/atarget", Case::Sensitive));
+ assert!(!match_file(pattern, "dir/targeta", Case::Sensitive));
+ assert!(match_path(pattern, "dir/target", Some(true), Case::Sensitive));
+
+ let pattern = &pat("target/");
+ assert!(!match_file(pattern, "dir/target", Case::Sensitive));
+ assert!(
+ !match_path(pattern, "dir/target", None, Case::Sensitive),
+ "it assumes unknown to not be a directory"
+ );
+ assert!(match_path(pattern, "dir/target", Some(true), Case::Sensitive));
+ assert!(
+ !match_path(pattern, "dir/target/", Some(true), Case::Sensitive),
+ "we need sanitized paths that don't have trailing slashes"
+ );
+}
+
+fn pat<'a>(pattern: impl Into<&'a BStr>) -> gix_glob::Pattern {
+ gix_glob::Pattern::from_bytes(pattern.into()).expect("parsing works")
+}
+
+fn match_file<'a>(pattern: &gix_glob::Pattern, path: impl Into<&'a BStr>, case: Case) -> bool {
+ match_path(pattern, path, false.into(), case)
+}
+
+fn match_path<'a>(pattern: &gix_glob::Pattern, path: impl Into<&'a BStr>, is_dir: Option<bool>, case: Case) -> bool {
+ let path = path.into();
+ pattern.matches_repo_relative_path(path, basename_start_pos(path), is_dir, case)
+}
+
+fn basename_start_pos(value: &BStr) -> Option<usize> {
+ value.rfind_byte(b'/').map(|pos| pos + 1)
+}
diff --git a/vendor/gix-glob/tests/pattern/mod.rs b/vendor/gix-glob/tests/pattern/mod.rs
new file mode 100644
index 000000000..ca5d8cbad
--- /dev/null
+++ b/vendor/gix-glob/tests/pattern/mod.rs
@@ -0,0 +1,18 @@
+use gix_glob::{pattern::Mode, Pattern};
+
+#[test]
+fn display() {
+ fn pat(text: &str, mode: Mode) -> String {
+ Pattern {
+ text: text.into(),
+ mode,
+ first_wildcard_pos: None,
+ }
+ .to_string()
+ }
+ assert_eq!(pat("a", Mode::ABSOLUTE), "/a");
+ assert_eq!(pat("a", Mode::MUST_BE_DIR), "a/");
+ assert_eq!(pat("a", Mode::NEGATIVE), "!a");
+ assert_eq!(pat("a", Mode::ABSOLUTE | Mode::NEGATIVE | Mode::MUST_BE_DIR), "!/a/");
+}
+mod matching;
diff --git a/vendor/gix-glob/tests/wildmatch/mod.rs b/vendor/gix-glob/tests/wildmatch/mod.rs
new file mode 100644
index 000000000..2e74dabf3
--- /dev/null
+++ b/vendor/gix-glob/tests/wildmatch/mod.rs
@@ -0,0 +1,374 @@
+use std::{
+ fmt::{Debug, Display, Formatter},
+ panic::catch_unwind,
+};
+
+use bstr::ByteSlice;
+use gix_glob::{pattern::Case, wildmatch, Pattern};
+
+#[test]
+fn corpus() {
+ // based on git/t/t3070.sh
+ let tests = [
+ (1u8,1u8,1u8,1u8, "foo", "foo"),
+ (0,0,0,0, "foo", "bar"),
+ (1,1,1,1, "foo", "???"),
+ (0,0,0,0, "foo", "??"),
+ (1,1,1,1, "foo", "*"),
+ (1,1,1,1, "foo", "f*"),
+ (0,0,0,0, "foo", "*f"),
+ (1,1,1,1, "foo", "*foo*"),
+ (1,1,1,1, "foobar", "*ob*a*r*"),
+ (1,1,1,1, "aaaaaaabababab", "*ab"),
+ (1,1,1,1, "foo*", r"foo\*"),
+ (0,0,0,0, "foobar", r"foo\*bar"),
+ (1,1,1,1, r"f\oo", r"f\\oo"),
+ (1,1,1,1, "ball", "*[al]?"),
+ (0,0,0,0, "ten", "[ten]"),
+ (1,1,1,1, "ten", "**[!te]"),
+ (0,0,0,0, "ten", "**[!ten]"),
+ (1,1,1,1, "ten", "t[a-g]n"),
+ (0,0,0,0, "ten", "t[!a-g]n"),
+ (1,1,1,1, "ton", "t[!a-g]n"),
+ (1,1,1,1, "ton", "t[^a-g]n"),
+ (1,1,1,1, "a]b", "a[]]b"),
+ (1,1,1,1, "a-b", "a[]-]b"),
+ (1,1,1,1, "a]b", "a[]-]b"),
+ (0,0,0,0, "aab", "a[]-]b"),
+ (1,1,1,1, "aab", "a[]a-]b"),
+ (1,1,1,1, "]", "]"),
+ // Extended slash-matching features
+ (0,0,1,1, "foo/baz/bar", "foo*bar"),
+ (0,0,1,1, "foo/baz/bar", "foo**bar"),
+ (1,1,1,1, "foobazbar", "foo**bar"),
+ (1,1,1,1, "foo/baz/bar", "foo/**/bar"),
+ (1,1,0,0, "foo/baz/bar", "foo/**/**/bar"),
+ (1,1,1,1, "foo/b/a/z/bar", "foo/**/bar"),
+ (1,1,1,1, "foo/b/a/z/bar", "foo/**/**/bar"),
+ (1,1,0,0, "foo/bar", "foo/**/bar"),
+ (1,1,0,0, "foo/bar", "foo/**/**/bar"),
+ (0,0,1,1, "foo/bar", "foo?bar"),
+ (0,0,1,1, "foo/bar", "foo[/]bar"),
+ (0,0,1,1, "foo/bar", "foo[^a-z]bar"),
+ (0,0,1,1, "foo/bar", "f[^eiu][^eiu][^eiu][^eiu][^eiu]r"),
+ (1,1,1,1, "foo-bar", "f[^eiu][^eiu][^eiu][^eiu][^eiu]r"),
+ (1,1,0,0, "foo", "**/foo"),
+ (1,1,1,1, "XXX/foo", "**/foo"),
+ (1,1,1,1, "bar/baz/foo", "**/foo"),
+ (0,0,1,1, "bar/baz/foo", "*/foo"),
+ (0,0,1,1, "foo/bar/baz", "**/bar*"),
+ (1,1,1,1, "deep/foo/bar/baz", "**/bar/*"),
+ (0,0,1,1, "deep/foo/bar/baz/", "**/bar/*"),
+ (1,1,1,1, "deep/foo/bar/baz/", "**/bar/**"),
+ (0,0,0,0, "deep/foo/bar", "**/bar/*"),
+ (1,1,1,1, "deep/foo/bar/", "**/bar/**"),
+ (0,0,1,1, "foo/bar/baz", "**/bar**"),
+ (1,1,1,1, "foo/bar/baz/x", "*/bar/**"),
+ (0,0,1,1, "deep/foo/bar/baz/x", "*/bar/**"),
+ (1,1,1,1, "deep/foo/bar/baz/x", "**/bar/*/*"),
+
+ // Various additional tests
+ (0,0,0,0, "acrt", "a[c-c]st"),
+ (1,1,1,1, "acrt", "a[c-c]rt"),
+ (0,0,0,0, "]", "[!]-]"),
+ (1,1,1,1, "a", "[!]-]"),
+ (0,0,0,0, "", r"\"),
+ (0,0,1,1, r"XXX/\", r"*/\"),
+ (0,0,1,1, r"XXX/\", r"*/\\"),
+ (1,1,1,1, "foo", "foo"),
+ (1,1,1,1, "@foo", "@foo"),
+ (0,0,0,0, "foo", "@foo"),
+ (1,1,1,1, "[ab]", r"\[ab]"),
+ (1,1,1,1, "[ab]", "[[]ab]"),
+ (1,1,1,1, "[ab]", "[[:]ab]"),
+ (0,0,0,0, "[ab]", "[[::]ab]"),
+ (1,1,1,1, "[ab]", "[[:digit]ab]"),
+ (1,1,1,1, "[ab]", r"[\[:]ab]"),
+ (1,1,1,1, "?a?b", r"\??\?b"),
+ (1,1,1,1, "abc", r"\a\b\c"),
+ (1,1,1,1, "foo/bar/baz/to", "**/t[o]"),
+
+ // Character class tests
+ (1,1,1,1, "a1B", "[[:alpha:]][[:digit:]][[:upper:]]"),
+ (0,1,0,1, "a", "[[:digit:][:upper:][:space:]]"),
+ (1,1,1,1, "A", "[[:digit:][:upper:][:space:]]"),
+ (1,1,1,1, "1", "[[:digit:][:upper:][:space:]]"),
+ (0,0,0,0, "1", "[[:digit:][:upper:][:spaci:]]"),
+ (1,1,1,1, " ", "[[:digit:][:upper:][:space:]]"),
+ (0,0,0,0, ".", "[[:digit:][:upper:][:space:]]"),
+ (1,1,1,1, ".", "[[:digit:][:punct:][:space:]]"),
+ (1,1,1,1, "5", "[[:xdigit:]]"),
+ (1,1,1,1, "f", "[[:xdigit:]]"),
+ (1,1,1,1, "D", "[[:xdigit:]]"),
+ (1,1,1,1, "_", "[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]"),
+ (1,1,1,1, ".", "[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]"),
+ (1,1,1,1, "5", "[a-c[:digit:]x-z]"),
+ (1,1,1,1, "b", "[a-c[:digit:]x-z]"),
+ (1,1,1,1, "y", "[a-c[:digit:]x-z]"),
+ (0,0,0,0, "q", "[a-c[:digit:]x-z]"),
+
+ // Additional tests, including some malformed wild(patterns
+ (1,1,1,1, "]", r"[\\-^]"),
+ (0,0,0,0, "[", r"[\\-^]"),
+ (1,1,1,1, "-", r"[\-_]"),
+ (1,1,1,1, "]", r"[\]]"),
+ (0,0,0,0, r"\]", r"[\]]"),
+ (0,0,0,0, r"\", r"[\]]"),
+ (0,0,0,0, "ab", "a[]b"),
+ (0,0,0,0, "ab", "[!"),
+ (0,0,0,0, "ab", "[-"),
+ (1,1,1,1, "-", "[-]"),
+ (0,0,0,0, "-", "[a-"),
+ (0,0,0,0, "-", "[!a-"),
+ (1,1,1,1, "-", "[--A]"),
+ (1,1,1,1, "5", "[--A]"),
+ (1,1,1,1, " ", "[ --]"),
+ (1,1,1,1, "$", "[ --]"),
+ (1,1,1,1, "-", "[ --]"),
+ (0,0,0,0, "0", "[ --]"),
+ (1,1,1,1, "-", "[---]"),
+ (1,1,1,1, "-", "[------]"),
+ (0,0,0,0, "j", "[a-e-n]"),
+ (1,1,1,1, "-", "[a-e-n]"),
+ (1,1,1,1, "a", "[!------]"),
+ (0,0,0,0, "[", "[]-a]"),
+ (1,1,1,1, "^", "[]-a]"),
+ (0,0,0,0, "^", "[!]-a]"),
+ (1,1,1,1, "[", "[!]-a]"),
+ (1,1,1,1, "^", "[a^bc]"),
+ (1,1,1,1, "-b]", "[a-]b]"),
+ (0,0,0,0, r"\", r"[\]"),
+ (1,1,1,1, r"\", r"[\\]"),
+ (0,0,0,0, r"\", r"[!\\]"),
+ (1,1,1,1, "G", r"[A-\\]"),
+ (0,0,0,0, "aaabbb", "b*a"),
+ (0,0,0,0, "aabcaa", "*ba*"),
+ (1,1,1,1, ",", "[,]"),
+ (1,1,1,1, ",", r"[\\,]"),
+ (1,1,1,1, r"\", r"[\\,]"),
+ (1,1,1,1, "-", "[,-.]"),
+ (0,0,0,0, "+", "[,-.]"),
+ (0,0,0,0, "-.]", "[,-.]"),
+ (1,1,1,1, "2", r"[\1-\3]"),
+ (1,1,1,1, "3", r"[\1-\3]"),
+ (0,0,0,0, "4", r"[\1-\3]"),
+ (1,1,1,1, r"\", r"[[-\]]"),
+ (1,1,1,1, "[", r"[[-\]]"),
+ (1,1,1,1, "]", r"[[-\]]"),
+ (0,0,0,0, "-", r"[[-\]]"),
+
+ // Test recursion
+ (1,1,1,1, "-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*"),
+ (0,0,0,0, "-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*"),
+ (0,0,0,0, "-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*"),
+ (1,1,1,1, "XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1", "XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*"),
+ (0,0,0,0, "XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1", "XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*"),
+ (1,1,1,1, "abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt", "**/*a*b*g*n*t"),
+ (0,0,0,0, "abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz", "**/*a*b*g*n*t"),
+ (0,0,0,0, "foo", "*/*/*"),
+ (0,0,0,0, "foo/bar", "*/*/*"),
+ (1,1,1,1, "foo/bba/arr", "*/*/*"),
+ (0,0,1,1, "foo/bb/aa/rr", "*/*/*"),
+ (1,1,1,1, "foo/bb/aa/rr", "**/**/**"),
+ (1,1,1,1, "abcXdefXghi", "*X*i"),
+ (0,0,1,1, "ab/cXd/efXg/hi", "*X*i"),
+ (1,1,1,1, "ab/cXd/efXg/hi", "*/*X*/*/*i"),
+ (1,1,1,1, "ab/cXd/efXg/hi", "**/*X*/**/*i"),
+
+ // Extra path(tests
+ (0,0,0,0, "foo", "fo"),
+ (1,1,1,1,"foo/bar", "foo/bar"),
+ (1,1,1,1, "foo/bar", "foo/*"),
+ (0,0,1,1, "foo/bba/arr", "foo/*"),
+ (1,1,1,1, "foo/bba/arr", "foo/**"),
+ (0,0,1,1, "foo/bba/arr", "foo*"),
+ (0,0,1,1, "foo/bba/arr", "foo/*arr"),
+ (0,0,1,1, "foo/bba/arr", "foo/**arr"),
+ (0,0,0,0, "foo/bba/arr", "foo/*z"),
+ (0,0,0,0, "foo/bba/arr", "foo/**z"),
+ (0,0,1,1, "foo/bar", "foo?bar"),
+ (0,0,1,1, "foo/bar", "foo[/]bar"),
+ (0,0,1,1, "foo/bar", "foo[^a-z]bar"),
+ (0,0,1,1, "ab/cXd/efXg/hi", "*Xg*i"),
+
+ // Extra case-sensitivity tests
+ (0,1,0,1, "a", "[A-Z]"),
+ (1,1,1,1, "A", "[A-Z]"),
+ (0,1,0,1, "A", "[a-z]"),
+ (1,1,1,1, "a", "[a-z]"),
+ (0,1,0,1, "a", "[[:upper:]]"),
+ (1,1,1,1, "A", "[[:upper:]]"),
+ (0,1,0,1, "A", "[[:lower:]]"),
+ (1,1,1,1, "a", "[[:lower:]]"),
+ (0,1,0,1, "A", "[B-Za]"),
+ (1,1,1,1, "a", "[B-Za]"),
+ (0,1,0,1, "A", "[B-a]"),
+ (1,1,1,1, "a", "[B-a]"),
+ (0,1,0,1, "z", "[Z-y]"),
+ (1,1,1,1, "Z", "[Z-y]"),
+ ];
+
+ let mut failures = Vec::new();
+ let mut at_least_one_panic = 0;
+ for (path_match, path_imatch, glob_match, glob_imatch, text, pattern_text) in tests {
+ let (pattern, actual) = multi_match(pattern_text, text);
+ let expected = expect_multi(path_match, path_imatch, glob_match, glob_imatch);
+
+ if actual.all_panicked() {
+ at_least_one_panic += 1;
+ } else if actual != expected {
+ failures.push((pattern, pattern_text, text, actual, expected));
+ } else {
+ at_least_one_panic += i32::from(actual.any_panicked());
+ }
+ }
+
+ dbg!(&failures);
+ assert_eq!(failures.len(), 0);
+ assert_eq!(at_least_one_panic, 0, "not a single panic in any invocation");
+
+ // TODO: reproduce these
+ // (0 0 0 0 \
+ // 1 1 1 1 '\' '\'
+ // (0 0 0 0 \
+ // E E E E 'foo' ''
+ // (0 0 0 0 \
+ // 1 1 1 1 'a[]b' 'a[]b'
+ // (0 0 0 0 \
+ // 1 1 1 1 'ab[' 'ab['
+ // (0 0 1 1 \
+ // 1 1 1 1 foo/bba/arr 'foo**'
+}
+
+#[test]
+fn brackets() {
+ let (_pattern, actual) = multi_match(r"[B-a]", "A");
+ assert!(!actual.any_panicked());
+ assert_eq!(actual, expect_multi(0, 1, 0, 1));
+}
+
+fn multi_match(pattern_text: &str, text: &str) -> (Pattern, MultiMatch) {
+ let pattern = gix_glob::Pattern::from_bytes(pattern_text.as_bytes()).expect("valid (enough) pattern");
+ let actual_path_match: MatchResult = catch_unwind(|| match_file_path(&pattern, text, Case::Sensitive)).into();
+ let actual_path_imatch: MatchResult = catch_unwind(|| match_file_path(&pattern, text, Case::Fold)).into();
+ let actual_glob_match: MatchResult =
+ catch_unwind(|| gix_glob::wildmatch(pattern.text.as_bstr(), text.into(), wildmatch::Mode::empty())).into();
+ let actual_glob_imatch: MatchResult =
+ catch_unwind(|| gix_glob::wildmatch(pattern.text.as_bstr(), text.into(), wildmatch::Mode::IGNORE_CASE)).into();
+ let actual = MultiMatch {
+ path_match: actual_path_match,
+ path_imatch: actual_path_imatch,
+ glob_match: actual_glob_match,
+ glob_imatch: actual_glob_imatch,
+ };
+ (pattern, actual)
+}
+
+fn expect_multi(path_match: u8, path_imatch: u8, glob_match: u8, glob_imatch: u8) -> MultiMatch {
+ (path_match, path_imatch, glob_match, glob_imatch).into()
+}
+
+#[derive(Eq, PartialEq)]
+struct MultiMatch {
+ path_match: MatchResult,
+ path_imatch: MatchResult,
+ glob_match: MatchResult,
+ glob_imatch: MatchResult,
+}
+
+impl MultiMatch {
+ fn all_panicked(&self) -> bool {
+ use MatchResult::Panic;
+ matches!(self.path_match, Panic)
+ && matches!(self.path_imatch, Panic)
+ && matches!(self.glob_match, Panic)
+ && matches!(self.glob_imatch, Panic)
+ }
+ fn any_panicked(&self) -> bool {
+ use MatchResult::Panic;
+ matches!(self.path_match, Panic)
+ || matches!(self.path_imatch, Panic)
+ || matches!(self.glob_match, Panic)
+ || matches!(self.glob_imatch, Panic)
+ }
+}
+
+impl From<(u8, u8, u8, u8)> for MultiMatch {
+ fn from(t: (u8, u8, u8, u8)) -> Self {
+ MultiMatch {
+ path_match: t.0.into(),
+ path_imatch: t.1.into(),
+ glob_match: t.2.into(),
+ glob_imatch: t.3.into(),
+ }
+ }
+}
+
+impl Debug for MultiMatch {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "({} {} {} {})",
+ self.path_match, self.path_imatch, self.glob_match, self.glob_imatch
+ )
+ }
+}
+
+enum MatchResult {
+ Match,
+ NoMatch,
+ Panic,
+}
+
+impl PartialEq<Self> for MatchResult {
+ fn eq(&self, other: &Self) -> bool {
+ use MatchResult::*;
+ match (self, other) {
+ (Panic, _) | (_, Panic) => true,
+ (Match, NoMatch) | (NoMatch, Match) => false,
+ (Match, Match) | (NoMatch, NoMatch) => true,
+ }
+ }
+}
+
+impl std::cmp::Eq for MatchResult {}
+
+impl From<std::thread::Result<bool>> for MatchResult {
+ fn from(v: std::thread::Result<bool>) -> Self {
+ use MatchResult::*;
+ match v {
+ Ok(v) if v => Match,
+ Ok(_) => NoMatch,
+ Err(_) => Panic,
+ }
+ }
+}
+
+impl From<u8> for MatchResult {
+ fn from(v: u8) -> Self {
+ use MatchResult::*;
+ match v {
+ 1 => Match,
+ 0 => NoMatch,
+ _ => unreachable!("BUG: only use 0 or 1 for expected values"),
+ }
+ }
+}
+
+impl Display for MatchResult {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ use MatchResult::*;
+ f.write_str(match self {
+ Match => "✔️",
+ NoMatch => "⨯",
+ Panic => "E",
+ })
+ }
+}
+
+fn match_file_path(pattern: &gix_glob::Pattern, path: &str, case: Case) -> bool {
+ pattern.matches_repo_relative_path(path, basename_of(path), false.into() /* is_dir */, case)
+}
+fn basename_of(path: &str) -> Option<usize> {
+ path.rfind('/').map(|pos| pos + 1)
+}