• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 extern crate which;
2 
3 #[cfg(all(unix, feature = "regex"))]
4 use regex::Regex;
5 use std::ffi::{OsStr, OsString};
6 use std::fs;
7 use std::io;
8 use std::path::{Path, PathBuf};
9 use std::{env, vec};
10 use tempfile::TempDir;
11 
12 struct TestFixture {
13     /// Temp directory.
14     pub tempdir: TempDir,
15     /// $PATH
16     pub paths: OsString,
17     /// Binaries created in $PATH
18     pub bins: Vec<PathBuf>,
19 }
20 
21 const SUBDIRS: &[&str] = &["a", "b", "c"];
22 const BIN_NAME: &str = "bin";
23 
24 #[cfg(unix)]
mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf>25 fn mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> {
26     use std::os::unix::fs::OpenOptionsExt;
27     let bin = dir.join(path).with_extension(extension);
28     fs::OpenOptions::new()
29         .write(true)
30         .create(true)
31         .mode(0o666 | (libc::S_IXUSR as u32))
32         .open(&bin)
33         .and_then(|_f| bin.canonicalize())
34 }
35 
touch(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf>36 fn touch(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> {
37     let b = dir.join(path).with_extension(extension);
38     fs::File::create(&b).and_then(|_f| b.canonicalize())
39 }
40 
41 #[cfg(windows)]
mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf>42 fn mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> {
43     touch(dir, path, extension)
44 }
45 
46 impl TestFixture {
47     // tmp/a/bin
48     // tmp/a/bin.exe
49     // tmp/a/bin.cmd
50     // tmp/b/bin
51     // tmp/b/bin.exe
52     // tmp/b/bin.cmd
53     // tmp/c/bin
54     // tmp/c/bin.exe
55     // tmp/c/bin.cmd
new() -> TestFixture56     pub fn new() -> TestFixture {
57         let tempdir = tempfile::tempdir().unwrap();
58         let mut builder = fs::DirBuilder::new();
59         builder.recursive(true);
60         let mut paths = vec![];
61         let mut bins = vec![];
62         for d in SUBDIRS.iter() {
63             let p = tempdir.path().join(d);
64             builder.create(&p).unwrap();
65             bins.push(mk_bin(&p, BIN_NAME, "").unwrap());
66             bins.push(mk_bin(&p, BIN_NAME, "exe").unwrap());
67             bins.push(mk_bin(&p, BIN_NAME, "cmd").unwrap());
68             paths.push(p);
69         }
70         let p = tempdir.path().join("win-bin");
71         builder.create(&p).unwrap();
72         bins.push(mk_bin(&p, "win-bin", "exe").unwrap());
73         paths.push(p);
74         TestFixture {
75             tempdir,
76             paths: env::join_paths(paths).unwrap(),
77             bins,
78         }
79     }
80 
81     #[allow(dead_code)]
touch(&self, path: &str, extension: &str) -> io::Result<PathBuf>82     pub fn touch(&self, path: &str, extension: &str) -> io::Result<PathBuf> {
83         touch(self.tempdir.path(), path, extension)
84     }
85 
mk_bin(&self, path: &str, extension: &str) -> io::Result<PathBuf>86     pub fn mk_bin(&self, path: &str, extension: &str) -> io::Result<PathBuf> {
87         mk_bin(self.tempdir.path(), path, extension)
88     }
89 }
90 
_which<T: AsRef<OsStr>>(f: &TestFixture, path: T) -> which::Result<which::CanonicalPath>91 fn _which<T: AsRef<OsStr>>(f: &TestFixture, path: T) -> which::Result<which::CanonicalPath> {
92     which::CanonicalPath::new_in(path, Some(f.paths.clone()), f.tempdir.path())
93 }
94 
_which_all<'a, T: AsRef<OsStr> + 'a>( f: &'a TestFixture, path: T, ) -> which::Result<impl Iterator<Item = which::Result<which::CanonicalPath>> + '_>95 fn _which_all<'a, T: AsRef<OsStr> + 'a>(
96     f: &'a TestFixture,
97     path: T,
98 ) -> which::Result<impl Iterator<Item = which::Result<which::CanonicalPath>> + '_> {
99     which::CanonicalPath::all_in(path, Some(f.paths.clone()), f.tempdir.path())
100 }
101 
102 #[test]
103 #[cfg(unix)]
it_works()104 fn it_works() {
105     use std::process::Command;
106     let result = which::Path::new("rustc");
107     assert!(result.is_ok());
108 
109     let which_result = Command::new("which").arg("rustc").output();
110 
111     assert_eq!(
112         String::from(result.unwrap().to_str().unwrap()),
113         String::from_utf8(which_result.unwrap().stdout)
114             .unwrap()
115             .trim()
116     );
117 }
118 
119 #[test]
120 #[cfg(unix)]
test_which()121 fn test_which() {
122     let f = TestFixture::new();
123     assert_eq!(_which(&f, &BIN_NAME).unwrap(), f.bins[0])
124 }
125 
126 #[test]
127 #[cfg(windows)]
test_which()128 fn test_which() {
129     let f = TestFixture::new();
130     assert_eq!(_which(&f, &BIN_NAME).unwrap(), f.bins[1])
131 }
132 
133 #[test]
134 #[cfg(all(unix, feature = "regex"))]
test_which_re_in_with_matches()135 fn test_which_re_in_with_matches() {
136     let f = TestFixture::new();
137     f.mk_bin("a/bin_0", "").unwrap();
138     f.mk_bin("b/bin_1", "").unwrap();
139     let re = Regex::new(r"bin_\d").unwrap();
140 
141     let result: Vec<PathBuf> = which::which_re_in(re, Some(f.paths))
142         .unwrap()
143         .into_iter()
144         .collect();
145 
146     let temp = f.tempdir;
147 
148     assert_eq!(
149         result,
150         vec![temp.path().join("a/bin_0"), temp.path().join("b/bin_1")]
151     )
152 }
153 
154 #[test]
155 #[cfg(all(unix, feature = "regex"))]
test_which_re_in_without_matches()156 fn test_which_re_in_without_matches() {
157     let f = TestFixture::new();
158     let re = Regex::new(r"bi[^n]").unwrap();
159 
160     let result: Vec<PathBuf> = which::which_re_in(re, Some(f.paths))
161         .unwrap()
162         .into_iter()
163         .collect();
164 
165     assert_eq!(result, Vec::<PathBuf>::new())
166 }
167 
168 #[test]
169 #[cfg(all(unix, feature = "regex"))]
test_which_re_accepts_owned_and_borrow()170 fn test_which_re_accepts_owned_and_borrow() {
171     which::which_re(Regex::new(r".").unwrap())
172         .unwrap()
173         .for_each(drop);
174     which::which_re(&Regex::new(r".").unwrap())
175         .unwrap()
176         .for_each(drop);
177     which::which_re_in(Regex::new(r".").unwrap(), Some("pth"))
178         .unwrap()
179         .for_each(drop);
180     which::which_re_in(&Regex::new(r".").unwrap(), Some("pth"))
181         .unwrap()
182         .for_each(drop);
183 }
184 
185 #[test]
186 #[cfg(unix)]
test_which_extension()187 fn test_which_extension() {
188     let f = TestFixture::new();
189     let b = Path::new(&BIN_NAME).with_extension("");
190     assert_eq!(_which(&f, &b).unwrap(), f.bins[0])
191 }
192 
193 #[test]
194 #[cfg(windows)]
test_which_extension()195 fn test_which_extension() {
196     let f = TestFixture::new();
197     let b = Path::new(&BIN_NAME).with_extension("cmd");
198     assert_eq!(_which(&f, &b).unwrap(), f.bins[2])
199 }
200 
201 #[test]
202 #[cfg(windows)]
test_which_no_extension()203 fn test_which_no_extension() {
204     let f = TestFixture::new();
205     let b = Path::new("win-bin");
206     let which_result = which::which_in(&b, Some(&f.paths), ".").unwrap();
207     // Make sure the extension is the correct case.
208     assert_eq!(which_result.extension(), f.bins[9].extension());
209     assert_eq!(fs::canonicalize(&which_result).unwrap(), f.bins[9])
210 }
211 
212 #[test]
test_which_not_found()213 fn test_which_not_found() {
214     let f = TestFixture::new();
215     assert!(_which(&f, "a").is_err());
216 }
217 
218 #[test]
test_which_second()219 fn test_which_second() {
220     let f = TestFixture::new();
221     let b = f.mk_bin("b/another", env::consts::EXE_EXTENSION).unwrap();
222     assert_eq!(_which(&f, "another").unwrap(), b);
223 }
224 
225 #[test]
test_which_all()226 fn test_which_all() {
227     let f = TestFixture::new();
228     let actual = _which_all(&f, BIN_NAME)
229         .unwrap()
230         .map(|c| c.unwrap())
231         .collect::<Vec<_>>();
232     let mut expected = f
233         .bins
234         .iter()
235         .map(|p| p.canonicalize().unwrap())
236         .collect::<Vec<_>>();
237     #[cfg(windows)]
238     {
239         expected.retain(|p| p.file_stem().unwrap() == BIN_NAME);
240         expected.retain(|p| p.extension().map(|ext| ext == "exe" || ext == "cmd") == Some(true));
241     }
242     #[cfg(not(windows))]
243     {
244         expected.retain(|p| p.file_name().unwrap() == BIN_NAME);
245     }
246     assert_eq!(actual, expected);
247 }
248 
249 #[test]
250 #[cfg(unix)]
test_which_absolute()251 fn test_which_absolute() {
252     let f = TestFixture::new();
253     assert_eq!(
254         _which(&f, &f.bins[3]).unwrap(),
255         f.bins[3].canonicalize().unwrap()
256     );
257 }
258 
259 #[test]
260 #[cfg(windows)]
test_which_absolute()261 fn test_which_absolute() {
262     let f = TestFixture::new();
263     assert_eq!(
264         _which(&f, &f.bins[4]).unwrap(),
265         f.bins[4].canonicalize().unwrap()
266     );
267 }
268 
269 #[test]
270 #[cfg(windows)]
test_which_absolute_path_case()271 fn test_which_absolute_path_case() {
272     // Test that an absolute path with an uppercase extension
273     // is accepted.
274     let f = TestFixture::new();
275     let p = &f.bins[4];
276     assert_eq!(_which(&f, &p).unwrap(), f.bins[4].canonicalize().unwrap());
277 }
278 
279 #[test]
280 #[cfg(unix)]
test_which_absolute_extension()281 fn test_which_absolute_extension() {
282     let f = TestFixture::new();
283     // Don't append EXE_EXTENSION here.
284     let b = f.bins[3].parent().unwrap().join(&BIN_NAME);
285     assert_eq!(_which(&f, &b).unwrap(), f.bins[3].canonicalize().unwrap());
286 }
287 
288 #[test]
289 #[cfg(windows)]
test_which_absolute_extension()290 fn test_which_absolute_extension() {
291     let f = TestFixture::new();
292     // Don't append EXE_EXTENSION here.
293     let b = f.bins[4].parent().unwrap().join(&BIN_NAME);
294     assert_eq!(_which(&f, &b).unwrap(), f.bins[4].canonicalize().unwrap());
295 }
296 
297 #[test]
298 #[cfg(unix)]
test_which_relative()299 fn test_which_relative() {
300     let f = TestFixture::new();
301     assert_eq!(
302         _which(&f, "b/bin").unwrap(),
303         f.bins[3].canonicalize().unwrap()
304     );
305 }
306 
307 #[test]
308 #[cfg(windows)]
test_which_relative()309 fn test_which_relative() {
310     let f = TestFixture::new();
311     assert_eq!(
312         _which(&f, "b/bin").unwrap(),
313         f.bins[4].canonicalize().unwrap()
314     );
315 }
316 
317 #[test]
318 #[cfg(unix)]
test_which_relative_extension()319 fn test_which_relative_extension() {
320     // test_which_relative tests a relative path without an extension,
321     // so test a relative path with an extension here.
322     let f = TestFixture::new();
323     let b = Path::new("b/bin").with_extension(env::consts::EXE_EXTENSION);
324     assert_eq!(_which(&f, &b).unwrap(), f.bins[3].canonicalize().unwrap());
325 }
326 
327 #[test]
328 #[cfg(windows)]
test_which_relative_extension()329 fn test_which_relative_extension() {
330     // test_which_relative tests a relative path without an extension,
331     // so test a relative path with an extension here.
332     let f = TestFixture::new();
333     let b = Path::new("b/bin").with_extension("cmd");
334     assert_eq!(_which(&f, &b).unwrap(), f.bins[5].canonicalize().unwrap());
335 }
336 
337 #[test]
338 #[cfg(windows)]
test_which_relative_extension_case()339 fn test_which_relative_extension_case() {
340     // Test that a relative path with an uppercase extension
341     // is accepted.
342     let f = TestFixture::new();
343     let b = Path::new("b/bin").with_extension("EXE");
344     assert_eq!(_which(&f, &b).unwrap(), f.bins[4].canonicalize().unwrap());
345 }
346 
347 #[test]
348 #[cfg(unix)]
test_which_relative_leading_dot()349 fn test_which_relative_leading_dot() {
350     let f = TestFixture::new();
351     assert_eq!(
352         _which(&f, "./b/bin").unwrap(),
353         f.bins[3].canonicalize().unwrap()
354     );
355 }
356 
357 #[test]
358 #[cfg(windows)]
test_which_relative_leading_dot()359 fn test_which_relative_leading_dot() {
360     let f = TestFixture::new();
361     assert_eq!(
362         _which(&f, "./b/bin").unwrap(),
363         f.bins[4].canonicalize().unwrap()
364     );
365 }
366 
367 #[test]
368 #[cfg(unix)]
test_which_non_executable()369 fn test_which_non_executable() {
370     // Shouldn't return non-executable files.
371     let f = TestFixture::new();
372     f.touch("b/another", "").unwrap();
373     assert!(_which(&f, "another").is_err());
374 }
375 
376 #[test]
377 #[cfg(unix)]
test_which_absolute_non_executable()378 fn test_which_absolute_non_executable() {
379     // Shouldn't return non-executable files, even if given an absolute path.
380     let f = TestFixture::new();
381     let b = f.touch("b/another", "").unwrap();
382     assert!(_which(&f, &b).is_err());
383 }
384 
385 #[test]
386 #[cfg(unix)]
test_which_relative_non_executable()387 fn test_which_relative_non_executable() {
388     // Shouldn't return non-executable files.
389     let f = TestFixture::new();
390     f.touch("b/another", "").unwrap();
391     assert!(_which(&f, "b/another").is_err());
392 }
393 
394 #[test]
test_failure()395 fn test_failure() {
396     let f = TestFixture::new();
397 
398     let run = || -> which::Result<PathBuf> {
399         let p = _which(&f, "./b/bin")?;
400         Ok(p.into_path_buf())
401     };
402 
403     let _ = run();
404 }
405