oxedyne/fe2o3/fe2o3_file/tests/glob.rs
8.1 KiB, 9 runs
created by r1870400018:20066, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | //! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\ |
| 2 | //! Anthropic Claude |
| 3 | |
| 4 | use oxedyne_fe2o3_file::glob::{ |
| 5 | Glob, |
| 6 | IgnoreFile, |
| 7 | }; |
| 8 | use oxedyne_fe2o3_text::secret; |
| 9 | |
| 10 | use oxedyne_fe2o3_core::{ |
| 11 | prelude::*, |
| 12 | test::test_it, |
| 13 | }; |
| 14 | |
| 15 | |
| 16 | fn glob(pattern: &str) -> Outcome<Glob> { |
| 17 | Glob::new(pattern.as_bytes()) |
| 18 | } |
| 19 | |
| 20 | pub fn test_glob(filter: &'static str) -> Outcome<()> { |
| 21 | |
| 22 | res!(test_it(filter, &["Star stays within one component 000", "all", "glob"], || { |
| 23 | let g = res!(glob("*.tmp")); |
| 24 | assert!(g.matches(b"a.tmp", false)); |
| 25 | assert!(g.matches(b"deep/down/b.tmp", false), "unanchored, so any depth"); |
| 26 | assert!(!g.matches(b"a.tmpx", false)); |
| 27 | let g = res!(glob("src/*.rs")); |
| 28 | assert!(g.matches(b"src/lib.rs", false)); |
| 29 | assert!(!g.matches(b"src/deep/lib.rs", false), "a star does not cross a slash"); |
| 30 | assert!(!g.matches(b"other/src/lib.rs", false), "an inner slash anchors"); |
| 31 | Ok(()) |
| 32 | })); |
| 33 | |
| 34 | res!(test_it(filter, &["Question mark and classes take one byte 000", "all", "glob"], || { |
| 35 | let g = res!(glob("a?c")); |
| 36 | assert!(g.matches(b"abc", false)); |
| 37 | assert!(g.matches(b"a\xffc", false), "one byte, not one character"); |
| 38 | assert!(!g.matches(b"ac", false)); |
| 39 | assert!(!g.matches(b"a/c", false), "never a slash"); |
| 40 | let g = res!(glob("v[0-9].txt")); |
| 41 | assert!(g.matches(b"v7.txt", false)); |
| 42 | assert!(!g.matches(b"vx.txt", false)); |
| 43 | let g = res!(glob("v[!0-9].txt")); |
| 44 | assert!(g.matches(b"vx.txt", false)); |
| 45 | assert!(!g.matches(b"v7.txt", false)); |
| 46 | let g = res!(glob("[]x]")); |
| 47 | assert!(g.matches(b"]", false), "a bracket first in a class is literal"); |
| 48 | assert!(g.matches(b"x", false)); |
| 49 | Ok(()) |
| 50 | })); |
| 51 | |
| 52 | res!(test_it(filter, &["Escapes make wildcards literal 000", "all", "glob"], || { |
| 53 | let g = res!(glob("a\\*b")); |
| 54 | assert!(g.matches(b"a*b", false)); |
| 55 | assert!(!g.matches(b"axb", false)); |
| 56 | let g = res!(glob("\\!important")); |
| 57 | assert!(!g.is_negated()); |
| 58 | assert!(g.matches(b"!important", false)); |
| 59 | let g = res!(glob("odd[")); |
| 60 | assert!(g.matches(b"odd[", false), "an unclosed class is a literal bracket"); |
| 61 | Ok(()) |
| 62 | })); |
| 63 | |
| 64 | res!(test_it(filter, &["Anchoring follows the slashes 000", "all", "glob"], || { |
| 65 | let g = res!(glob("/target")); |
| 66 | assert!(g.matches(b"target", false)); |
| 67 | assert!(!g.matches(b"sub/target", false), "a leading slash anchors to the root"); |
| 68 | let g = res!(glob("doc/notes.txt")); |
| 69 | assert!(g.matches(b"doc/notes.txt", false)); |
| 70 | assert!(!g.matches(b"sub/doc/notes.txt", false)); |
| 71 | let g = res!(glob("notes.txt")); |
| 72 | assert!(g.matches(b"sub/doc/notes.txt", false), "no slash, so any depth"); |
| 73 | Ok(()) |
| 74 | })); |
| 75 | |
| 76 | res!(test_it(filter, &["A trailing slash means directories only 000", "all", "glob"], || { |
| 77 | let g = res!(glob("build/")); |
| 78 | assert!(g.is_dir_only()); |
| 79 | assert!(g.matches(b"build", true)); |
| 80 | assert!(!g.matches(b"build", false), "a file of that name is not matched"); |
| 81 | assert!(g.matches(b"sub/build", true), "unanchored despite the trailing slash"); |
| 82 | Ok(()) |
| 83 | })); |
| 84 | |
| 85 | res!(test_it(filter, &["Double star spans directories 000", "all", "glob"], || { |
| 86 | let g = res!(glob("**/foo")); |
| 87 | assert!(g.matches(b"foo", false)); |
| 88 | assert!(g.matches(b"a/b/foo", false)); |
| 89 | let g = res!(glob("abc/**")); |
| 90 | assert!(g.matches(b"abc/x", false)); |
| 91 | assert!(g.matches(b"abc/x/y", false)); |
| 92 | assert!(!g.matches(b"abc", true), "a trailing double star names the inside"); |
| 93 | let g = res!(glob("a/**/b")); |
| 94 | assert!(g.matches(b"a/b", false), "zero directories between"); |
| 95 | assert!(g.matches(b"a/x/b", false)); |
| 96 | assert!(g.matches(b"a/x/y/b", false)); |
| 97 | assert!(!g.matches(b"a/xb", false)); |
| 98 | let g = res!(glob("a**b")); |
| 99 | assert!(g.matches(b"axyb", false)); |
| 100 | assert!(!g.matches(b"a/b", false), "amid other bytes it is an ordinary star"); |
| 101 | Ok(()) |
| 102 | })); |
| 103 | |
| 104 | res!(test_it(filter, &["The last matching rule wins 000", "all", "glob", "ignore"], || { |
| 105 | let f = IgnoreFile::parse(b"*.log\n!keep.log\n"); |
| 106 | assert_eq!(f.decides(b"debug.log", false), Some(true)); |
| 107 | assert_eq!(f.decides(b"keep.log", false), Some(false), "the negation is later"); |
| 108 | assert_eq!(f.decides(b"keep.txt", false), None, "no rule spoke"); |
| 109 | assert!(!f.ignores(b"keep.log", false)); |
| 110 | let f = IgnoreFile::parse(b"!keep.log\n*.log\n"); |
| 111 | assert!(f.ignores(b"keep.log", false), "order is everything"); |
| 112 | Ok(()) |
| 113 | })); |
| 114 | |
| 115 | res!(test_it(filter, &["An ignored directory swallows its contents 000", "all", "glob", "ignore"], || { |
| 116 | let f = IgnoreFile::parse(b"target/\n!target/keep.txt\n"); |
| 117 | assert!(f.excludes(b"target", true)); |
| 118 | assert!(f.excludes(b"target/debris.o", false)); |
| 119 | assert!(f.excludes(b"target/keep.txt", false), |
| 120 | "nothing inside an ignored directory can be re-included"); |
| 121 | assert!(!f.excludes(b"src/lib.rs", false)); |
| 122 | let f = IgnoreFile::parse(b"*.log\n!keep.log\n"); |
| 123 | assert!(!f.excludes(b"logs/keep.log", false), |
| 124 | "the parent directory is not ignored, so the negation holds"); |
| 125 | Ok(()) |
| 126 | })); |
| 127 | |
| 128 | res!(test_it(filter, &["Comments, blanks and spaces read as git reads them 000", "all", "glob", "ignore"], || { |
| 129 | let f = IgnoreFile::parse(b"# a comment\n\n\r\n*.tmp \n\\#hash\nspaced\\ \n"); |
| 130 | assert!(f.ignores(b"a.tmp", false), "trailing spaces are trimmed"); |
| 131 | assert!(f.ignores(b"#hash", false), "an escaped hash is a pattern"); |
| 132 | assert!(f.ignores(b"spaced ", false), "an escaped trailing space is kept"); |
| 133 | assert!(!f.ignores(b"spaced", false)); |
| 134 | assert!(IgnoreFile::parse(b"# only a comment\n\n").is_empty()); |
| 135 | Ok(()) |
| 136 | })); |
| 137 | |
| 138 | res!(test_it(filter, &["Patterns and paths are bytes 000", "all", "glob", "ignore"], || { |
| 139 | // A path that is not UTF-8 is matched byte for byte. |
| 140 | let g = res!(glob("*.bin")); |
| 141 | assert!(g.matches(b"\xff\xfe.bin", false)); |
| 142 | let g = res!(Glob::new(b"?\xff*")); |
| 143 | assert!(g.matches(b"a\xff\xfe", false)); |
| 144 | assert!(!g.matches(b"a\xfe", false)); |
| 145 | // A pattern that is not UTF-8 compiles and matches likewise. |
| 146 | let f = IgnoreFile::parse(b"\xff*/\n"); |
| 147 | assert!(f.excludes(b"\xff\xfe/inside.txt", false)); |
| 148 | assert!(!f.excludes(b"\xfe/inside.txt", false)); |
| 149 | Ok(()) |
| 150 | })); |
| 151 | |
| 152 | res!(test_it(filter, &["A pattern with nothing to say is refused 000", "all", "glob"], || { |
| 153 | assert!(Glob::new(b"").is_err()); |
| 154 | assert!(Glob::new(b"!").is_err()); |
| 155 | assert!(Glob::new(b"/").is_err()); |
| 156 | Ok(()) |
| 157 | })); |
| 158 | |
| 159 | // The list lives upstream of this matcher, so the proof that every line of it is a rule, and |
| 160 | // that the rules decide what the list's comment says, has to stand here. |
| 161 | res!(test_it(filter, &["The secret paths are rules, and decide what they say 000", "all", |
| 162 | "glob", "secret"], || |
| 163 | { |
| 164 | for line in secret::SECRET_PATHS { |
| 165 | res!(Glob::new(line.as_bytes())); |
| 166 | } |
| 167 | let f = IgnoreFile::parse(secret::SECRET_PATHS.join("\n").as_bytes()); |
| 168 | for path in [ |
| 169 | ".env", "app/.env.local", ".env.production", |
| 170 | "server.pem", "cert/server.key", "store.p12", "store.pfx", "app.jks", "app.keystore", |
| 171 | "id_rsa", ".ssh/id_ed25519", ".netrc", ".pgpass", ".htpasswd", |
| 172 | "keys/anything.txt", "deep/tls/chain.txt", |
| 173 | ] { |
| 174 | assert!(f.excludes(path.as_bytes(), false), "{} is a secret by name", path); |
| 175 | } |
| 176 | for path in [ |
| 177 | ".env.example", ".env.sample", ".env.template", "environment.rs", "envoy.yaml", |
| 178 | "id_ed25519.pub", "server.pub", "server.crt", "README.md", "src/keys.rs", |
| 179 | "keys.txt", "tls.rs", "monkeys/banana.txt", |
| 180 | ] { |
| 181 | assert!(!f.excludes(path.as_bytes(), false), "{} is not", path); |
| 182 | } |
| 183 | assert!(f.excludes(b"keys", true), "the directory itself"); |
| 184 | assert!(!f.excludes(b"keys", false), "and not a file of that name"); |
| 185 | Ok(()) |
| 186 | })); |
| 187 | |
| 188 | Ok(()) |
| 189 | } |