Oregami
Repositories/oxedyne/fe2o3

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
4use oxedyne_fe2o3_file::glob::{
5 Glob,
6 IgnoreFile,
7};
8use oxedyne_fe2o3_text::secret;
9
10use oxedyne_fe2o3_core::{
11 prelude::*,
12 test::test_it,
13};
14
15
16fn glob(pattern: &str) -> Outcome<Glob> {
17 Glob::new(pattern.as_bytes())
18}
19
20pub 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}