use super::path_to_regex;
#[cfg(test)]
use crate::path;
fn assert_path_match(pattern: &str, paths: &str, expected: bool) {
let parsed = match path::parse(pattern) {
Ok(p) => p,
Err(e) => panic!("failed to parse pattern '{}': {:?}", pattern, e),
};
let re = match path_to_regex(&parsed) {
Ok(r) => r,
Err(e) => panic!("failed to build regex for pattern '{}': {}", pattern, e),
};
let matches = re.is_match(paths);
assert_eq!(
matches, expected,
"Pattern '{}' vs '{}' -> {} (expected {})",
pattern, paths, matches, expected
);
}
#[test]
fn test_basic_wildcards() {
assert_path_match("*", "README.md", true);
assert_path_match("*", "server.rb", true);
assert_path_match("*", "docs/file.txt", false); }
#[test]
fn test_question_mark_wildcard() {
assert_path_match("*.jsx?", "page.js", true);
assert_path_match("*.jsx?", "page.jsx", true);
assert_path_match("*.jsx?", "component.js", true);
assert_path_match("*.jsx?", "component.jsx", true);
assert_path_match("*.jsx?", "page.jsxx", false); assert_path_match("*.jsx?", "page.ts", false); }
#[test]
fn test_double_star_wildcard() {
assert_path_match("**", "all/the/files.md", true);
assert_path_match("**", "README.md", true);
assert_path_match("**", "docs/nested/deeply/file.txt", true);
assert_path_match("**", "single-file.js", true);
assert_path_match("**", "", true);
assert_path_match("**/a+", "a", true); assert_path_match("**/", "dir/", true); assert_path_match("**/", "file.txt", true);
assert_path_match("**/", "dir/file.txt", true);
assert_path_match("**/*", "dir/file.txt", true);
assert_path_match("**/*", "dir/another/file.txt", true);
}
#[test]
fn test_js_extension_pattern() {
assert_path_match("*.js", "app.js", true);
assert_path_match("*.js", "index.js", true);
assert_path_match("*.js", "main.js", true);
assert_path_match("*.js", "component.jsx", false); assert_path_match("*.js", "src/app.js", false); assert_path_match("*.js", "docs/script.js", false); }
#[test]
fn test_double_star_js_extension_pattern() {
assert_path_match("**.js", "index.js", true);
assert_path_match("**.js", "js/index.js", true);
assert_path_match("**.js", "src/js/app.js", true);
assert_path_match("**.js", "deeply/nested/path/to/file.js", true);
assert_path_match("**.js", "component.jsx", false); assert_path_match("**.js", "app.ts", false); assert_path_match("**.js", "script.js.backup", false); }
#[test]
fn test_docs_directory_pattern() {
assert_path_match("docs/*", "docs/README.md", true);
assert_path_match("docs/*", "docs/file.txt", true);
assert_path_match("docs/*", "docs/guide.md", true);
assert_path_match("docs/*", "docs/nested/file.txt", false); assert_path_match("docs/*", "README.md", false); assert_path_match("docs/*", "src/docs/file.txt", false); assert_path_match("docs/*", "docs", false); }
#[test]
fn test_docs_recursive_pattern() {
assert_path_match("docs/**", "docs/README.md", true);
assert_path_match("docs/**", "docs/mona/octocat.txt", true);
assert_path_match("docs/**", "docs/nested/deeply/file.txt", true);
assert_path_match("docs/**", "docs/guide.md", true);
assert_path_match("docs/**", "README.md", false); assert_path_match("docs/**", "src/docs/file.txt", false); assert_path_match("docs/**", "other/docs/file.txt", false); }
#[test]
fn test_docs_markdown_pattern() {
assert_path_match("docs/**/*.md", "docs/README.md", true);
assert_path_match("docs/**/*.md", "docs/mona/hello-world.md", true);
assert_path_match("docs/**/*.md", "docs/a/markdown/file.md", true);
assert_path_match("docs/**/*.md", "docs/nested/deeply/guide.md", true);
assert_path_match("docs/**/*.md", "docs/file.txt", false); assert_path_match("docs/**/*.md", "README.md", false); assert_path_match("docs/**/*.md", "src/docs/README.md", false); assert_path_match("docs/**/*.md", "docs/", false); }
#[test]
fn test_nested_docs_pattern() {
assert_path_match("**/docs/**", "docs/hello.md", true);
assert_path_match("**/docs/**", "dir/docs/my-file.txt", true);
assert_path_match("**/docs/**", "space/docs/plan/space.doc", true);
assert_path_match("**/docs/**", "project/nested/docs/README.md", true);
assert_path_match("**/docs/**", "docs/nested/deeply/file.txt", true);
assert_path_match("**/docs/**", "some/path/docs/guide.md", true);
assert_path_match("**/docs/**", "README.md", false); assert_path_match("**/docs/**", "documentation/file.txt", false); assert_path_match("**/docs/**", "docs-backup/file.txt", false); }
#[test]
fn test_readme_anywhere_pattern() {
assert_path_match("**/README.md", "README.md", true);
assert_path_match("**/README.md", "js/README.md", true);
assert_path_match("**/README.md", "docs/README.md", true);
assert_path_match("**/README.md", "src/components/README.md", true);
assert_path_match("**/README.md", "deeply/nested/path/README.md", true);
assert_path_match("**/README.md", "readme.md", false); assert_path_match("**/README.md", "README.txt", false); assert_path_match("**/README.md", "MY-README.md", false); assert_path_match("**/README.md", "docs/readme/file.md", false); }
#[test]
fn test_src_suffix_pattern() {
assert_path_match("**/*src/**", "a/src/app.js", true);
assert_path_match("**/*src/**", "my-src/code/js/app.js", true);
assert_path_match("**/*src/**", "project/main-src/utils/helper.js", true);
assert_path_match("**/*src/**", "app-src/components/Button.tsx", true);
assert_path_match("**/*src/**", "nested/path/web-src/styles/main.css", true);
assert_path_match("**/*src/**", "src/app.js", true);
assert_path_match("**/*src/**", "source/app.js", false); assert_path_match("**/*src/**", "src-backup/app.js", false); assert_path_match("**/*src/**", "app.js", false); assert_path_match("**/*src/**", "docs/src-old/file.txt", false); }
#[test]
fn test_post_suffix_pattern() {
assert_path_match("**/*-post.md", "my-post.md", true);
assert_path_match("**/*-post.md", "path/their-post.md", true);
assert_path_match("**/*-post.md", "blog/first-post.md", true);
assert_path_match("**/*-post.md", "docs/welcome-post.md", true);
assert_path_match("**/*-post.md", "nested/path/to/final-post.md", true);
assert_path_match("**/*-post.md", "-post.md", true);
assert_path_match("**/*-post.md", "post.md", false); assert_path_match("**/*-post.md", "my-post.txt", false); assert_path_match("**/*-post.md", "my-post-draft.md", false); assert_path_match("**/*-post.md", "posts/readme.md", false); }
#[test]
fn test_migrate_prefix_pattern() {
assert_path_match("**/migrate-*.sql", "migrate-10909.sql", true);
assert_path_match("**/migrate-*.sql", "db/migrate-v1.0.sql", true);
assert_path_match("**/migrate-*.sql", "db/sept/migrate-v1.sql", true);
assert_path_match("**/migrate-*.sql", "project/migrations/migrate-001.sql", true);
assert_path_match("**/migrate-*.sql", "migrate-initial.sql", true);
assert_path_match("**/migrate-*.sql", "migrate-.sql", true); assert_path_match("**/migrate-*.sql", "migration-v1.sql", false); assert_path_match("**/migrate-*.sql", "migrate-v1.txt", false); assert_path_match("**/migrate-*.sql", "migrate.sql", false); assert_path_match("**/migrate-*.sql", "db/migrate-v1.sql.backup", false);
}
#[test]
fn test_single_star_behavior() {
assert_path_match("Octo*", "Octocat", true);
assert_path_match("Octo*", "Octo", true);
assert_path_match("Octo*", "Octo/cat", false); assert_path_match("*.js", "dir/app.js", false); assert_path_match("*", "dir/file.txt", false); }
#[test]
fn test_double_star_behavior() {
assert_path_match("**", "anything", true);
assert_path_match("**", "dir/file.txt", true);
assert_path_match("**", "deep/nested/path/file.js", true);
assert_path_match("**", "", true);
}
#[test]
fn test_question_mark_behavior() {
assert_path_match("*.jsx?", "component.js", true);
assert_path_match("*.jsx?", "component.jsx", true);
assert_path_match("*.jsx?", "component.jsxx", false);
assert_path_match("file?.txt", "fil.txt", true);
assert_path_match("file?.txt", "file.txt", true);
assert_path_match("file?.txt", "filee.txt", false); }
#[test]
fn test_plus_behavior() {
assert_path_match("*.jsx+", "component.jsx", true);
assert_path_match("*.jsx+", "component.jsxx", true);
assert_path_match("*.jsx+", "component.jsxxx", true);
assert_path_match("*.jsx+", "component.js", false);
assert_path_match("file+.txt", "file.txt", true);
assert_path_match("file+.txt", "filee.txt", true);
assert_path_match("file+.txt", "fileee.txt", true);
assert_path_match("file+.txt", "fil.txt", false); }
#[test]
fn test_bracket_behavior() {
assert_path_match("[CB]at", "Cat", true);
assert_path_match("[CB]at", "Bat", true);
assert_path_match("[CB]at", "Dat", false);
assert_path_match("[CB]at", "at", false);
assert_path_match("[1-2]00", "100", true);
assert_path_match("[1-2]00", "200", true);
assert_path_match("[1-2]00", "300", false);
assert_path_match("[1-2]00", "000", false);
assert_path_match("file[a-c].txt", "filea.txt", true);
assert_path_match("file[a-c].txt", "fileb.txt", true);
assert_path_match("file[a-c].txt", "filec.txt", true);
assert_path_match("file[a-c].txt", "filed.txt", false);
assert_path_match("File[A-C].txt", "FileA.txt", true);
assert_path_match("File[A-C].txt", "FileB.txt", true);
assert_path_match("File[A-C].txt", "FileC.txt", true);
assert_path_match("File[A-C].txt", "FileD.txt", false);
assert_path_match("[0-9a-z]", "5", true);
assert_path_match("[0-9a-z]", "a", true);
assert_path_match("[0-9a-z]", "z", true);
assert_path_match("[0-9a-z]", "A", false); assert_path_match("[0-9a-z]", "@", false);
assert_path_match("[0-9a-zA-Z]", "5", true);
assert_path_match("[0-9a-zA-Z]", "a", true);
assert_path_match("[0-9a-zA-Z]", "z", true);
assert_path_match("[0-9a-zA-Z]", "A", true);
assert_path_match("[0-9a-zA-Z]", "Z", true);
assert_path_match("[0-9a-zA-Z]", "@", false); assert_path_match("[0-9a-zA-Z]", "-", false);
assert_path_match("[AB][ab][12]", "Aa1", true);
assert_path_match("[AB][ab][12]", "Ab2", true);
assert_path_match("[AB][ab][12]", "Ba1", true);
assert_path_match("[AB][ab][12]", "Bb2", true);
assert_path_match("[AB][ab][12]", "AA1", false); assert_path_match("[AB][ab][12]", "Aa3", false); assert_path_match("[AB][ab][12]", "Ca1", false);
assert_path_match("test[a].txt", "testa.txt", true);
}
fn assert_glob_compile_fail(pattern: &str) {
match path::parse(pattern) {
Err(_) => return, Ok(parsed) => {
if path_to_regex(&parsed).is_ok() {
panic!("expected pattern '{}' to fail regex compilation, but it compiled", pattern)
}
}
}
}
#[test]
fn test_regex_compile_failures() {
assert_glob_compile_fail("[]");
assert_glob_compile_fail("[^]");
assert_glob_compile_fail(r#"[\]"#);
}
#[test]
fn test_regex_github_compat() {
assert_glob_compile_fail("[-]");
assert_glob_compile_fail("[A-]");
assert_glob_compile_fail("[-A]");
}