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