| extern crate which; | |
| #[cfg(all(unix, feature = "regex"))] | |
| use regex::Regex; | |
| use std::ffi::{OsStr, OsString}; | |
| use std::fs; | |
| use std::io; | |
| use std::path::{Path, PathBuf}; | |
| use std::{env, vec}; | |
| use tempfile::TempDir; | |
| struct TestFixture { | |
| /// Temp directory. | |
| pub tempdir: TempDir, | |
| /// $PATH | |
| pub paths: OsString, | |
| /// Binaries created in $PATH | |
| pub bins: Vec<PathBuf>, | |
| } | |
| const SUBDIRS: &[&str] = &["a", "b", "c"]; | |
| const BIN_NAME: &str = "bin"; | |
| #[cfg(unix)] | |
| fn mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> { | |
| use std::os::unix::fs::OpenOptionsExt; | |
| let bin = dir.join(path).with_extension(extension); | |
| fs::OpenOptions::new() | |
| .write(true) | |
| .create(true) | |
| .mode(0o666 | (libc::S_IXUSR as u32)) | |
| .open(&bin) | |
| .and_then(|_f| bin.canonicalize()) | |
| } | |
| fn touch(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> { | |
| let b = dir.join(path).with_extension(extension); | |
| fs::File::create(&b).and_then(|_f| b.canonicalize()) | |
| } | |
| #[cfg(windows)] | |
| fn mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> { | |
| touch(dir, path, extension) | |
| } | |
| impl TestFixture { | |
| // tmp/a/bin | |
| // tmp/a/bin.exe | |
| // tmp/a/bin.cmd | |
| // tmp/b/bin | |
| // tmp/b/bin.exe | |
| // tmp/b/bin.cmd | |
| // tmp/c/bin | |
| // tmp/c/bin.exe | |
| // tmp/c/bin.cmd | |
| pub fn new() -> TestFixture { | |
| let tempdir = tempfile::tempdir().unwrap(); | |
| let mut builder = fs::DirBuilder::new(); | |
| builder.recursive(true); | |
| let mut paths = vec![]; | |
| let mut bins = vec![]; | |
| for d in SUBDIRS.iter() { | |
| let p = tempdir.path().join(d); | |
| builder.create(&p).unwrap(); | |
| bins.push(mk_bin(&p, BIN_NAME, "").unwrap()); | |
| bins.push(mk_bin(&p, BIN_NAME, "exe").unwrap()); | |
| bins.push(mk_bin(&p, BIN_NAME, "cmd").unwrap()); | |
| paths.push(p); | |
| } | |
| let p = tempdir.path().join("win-bin"); | |
| builder.create(&p).unwrap(); | |
| bins.push(mk_bin(&p, "win-bin", "exe").unwrap()); | |
| paths.push(p); | |
| TestFixture { | |
| tempdir, | |
| paths: env::join_paths(paths).unwrap(), | |
| bins, | |
| } | |
| } | |
| #[allow(dead_code)] | |
| pub fn touch(&self, path: &str, extension: &str) -> io::Result<PathBuf> { | |
| touch(self.tempdir.path(), path, extension) | |
| } | |
| pub fn mk_bin(&self, path: &str, extension: &str) -> io::Result<PathBuf> { | |
| mk_bin(self.tempdir.path(), path, extension) | |
| } | |
| } | |
| fn _which<T: AsRef<OsStr>>(f: &TestFixture, path: T) -> which::Result<which::CanonicalPath> { | |
| which::CanonicalPath::new_in(path, Some(f.paths.clone()), f.tempdir.path()) | |
| } | |
| fn _which_all<'a, T: AsRef<OsStr> + 'a>( | |
| f: &'a TestFixture, | |
| path: T, | |
| ) -> which::Result<impl Iterator<Item = which::Result<which::CanonicalPath>> + '_> { | |
| which::CanonicalPath::all_in(path, Some(f.paths.clone()), f.tempdir.path()) | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn it_works() { | |
| use std::process::Command; | |
| let result = which::Path::new("rustc"); | |
| assert!(result.is_ok()); | |
| let which_result = Command::new("which").arg("rustc").output(); | |
| assert_eq!( | |
| String::from(result.unwrap().to_str().unwrap()), | |
| String::from_utf8(which_result.unwrap().stdout) | |
| .unwrap() | |
| .trim() | |
| ); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which() { | |
| let f = TestFixture::new(); | |
| assert_eq!(_which(&f, &BIN_NAME).unwrap(), f.bins[0]) | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which() { | |
| let f = TestFixture::new(); | |
| assert_eq!(_which(&f, &BIN_NAME).unwrap(), f.bins[1]) | |
| } | |
| #[test] | |
| #[cfg(all(unix, feature = "regex"))] | |
| fn test_which_re_in_with_matches() { | |
| let f = TestFixture::new(); | |
| f.mk_bin("a/bin_0", "").unwrap(); | |
| f.mk_bin("b/bin_1", "").unwrap(); | |
| let re = Regex::new(r"bin_\d").unwrap(); | |
| let result: Vec<PathBuf> = which::which_re_in(re, Some(f.paths)) | |
| .unwrap() | |
| .into_iter() | |
| .collect(); | |
| let temp = f.tempdir; | |
| assert_eq!( | |
| result, | |
| vec![temp.path().join("a/bin_0"), temp.path().join("b/bin_1")] | |
| ) | |
| } | |
| #[test] | |
| #[cfg(all(unix, feature = "regex"))] | |
| fn test_which_re_in_without_matches() { | |
| let f = TestFixture::new(); | |
| let re = Regex::new(r"bi[^n]").unwrap(); | |
| let result: Vec<PathBuf> = which::which_re_in(re, Some(f.paths)) | |
| .unwrap() | |
| .into_iter() | |
| .collect(); | |
| assert_eq!(result, Vec::<PathBuf>::new()) | |
| } | |
| #[test] | |
| #[cfg(all(unix, feature = "regex"))] | |
| fn test_which_re_accepts_owned_and_borrow() { | |
| which::which_re(Regex::new(r".").unwrap()) | |
| .unwrap() | |
| .for_each(drop); | |
| which::which_re(&Regex::new(r".").unwrap()) | |
| .unwrap() | |
| .for_each(drop); | |
| which::which_re_in(Regex::new(r".").unwrap(), Some("pth")) | |
| .unwrap() | |
| .for_each(drop); | |
| which::which_re_in(&Regex::new(r".").unwrap(), Some("pth")) | |
| .unwrap() | |
| .for_each(drop); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which_extension() { | |
| let f = TestFixture::new(); | |
| let b = Path::new(&BIN_NAME).with_extension(""); | |
| assert_eq!(_which(&f, &b).unwrap(), f.bins[0]) | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which_extension() { | |
| let f = TestFixture::new(); | |
| let b = Path::new(&BIN_NAME).with_extension("cmd"); | |
| assert_eq!(_which(&f, &b).unwrap(), f.bins[2]) | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which_no_extension() { | |
| let f = TestFixture::new(); | |
| let b = Path::new("win-bin"); | |
| let which_result = which::which_in(&b, Some(&f.paths), ".").unwrap(); | |
| // Make sure the extension is the correct case. | |
| assert_eq!(which_result.extension(), f.bins[9].extension()); | |
| assert_eq!(fs::canonicalize(&which_result).unwrap(), f.bins[9]) | |
| } | |
| #[test] | |
| fn test_which_not_found() { | |
| let f = TestFixture::new(); | |
| assert!(_which(&f, "a").is_err()); | |
| } | |
| #[test] | |
| fn test_which_second() { | |
| let f = TestFixture::new(); | |
| let b = f.mk_bin("b/another", env::consts::EXE_EXTENSION).unwrap(); | |
| assert_eq!(_which(&f, "another").unwrap(), b); | |
| } | |
| #[test] | |
| fn test_which_all() { | |
| let f = TestFixture::new(); | |
| let actual = _which_all(&f, BIN_NAME) | |
| .unwrap() | |
| .map(|c| c.unwrap()) | |
| .collect::<Vec<_>>(); | |
| let mut expected = f | |
| .bins | |
| .iter() | |
| .map(|p| p.canonicalize().unwrap()) | |
| .collect::<Vec<_>>(); | |
| #[cfg(windows)] | |
| { | |
| expected.retain(|p| p.file_stem().unwrap() == BIN_NAME); | |
| expected.retain(|p| p.extension().map(|ext| ext == "exe" || ext == "cmd") == Some(true)); | |
| } | |
| #[cfg(not(windows))] | |
| { | |
| expected.retain(|p| p.file_name().unwrap() == BIN_NAME); | |
| } | |
| assert_eq!(actual, expected); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which_absolute() { | |
| let f = TestFixture::new(); | |
| assert_eq!( | |
| _which(&f, &f.bins[3]).unwrap(), | |
| f.bins[3].canonicalize().unwrap() | |
| ); | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which_absolute() { | |
| let f = TestFixture::new(); | |
| assert_eq!( | |
| _which(&f, &f.bins[4]).unwrap(), | |
| f.bins[4].canonicalize().unwrap() | |
| ); | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which_absolute_path_case() { | |
| // Test that an absolute path with an uppercase extension | |
| // is accepted. | |
| let f = TestFixture::new(); | |
| let p = &f.bins[4]; | |
| assert_eq!(_which(&f, &p).unwrap(), f.bins[4].canonicalize().unwrap()); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which_absolute_extension() { | |
| let f = TestFixture::new(); | |
| // Don't append EXE_EXTENSION here. | |
| let b = f.bins[3].parent().unwrap().join(&BIN_NAME); | |
| assert_eq!(_which(&f, &b).unwrap(), f.bins[3].canonicalize().unwrap()); | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which_absolute_extension() { | |
| let f = TestFixture::new(); | |
| // Don't append EXE_EXTENSION here. | |
| let b = f.bins[4].parent().unwrap().join(&BIN_NAME); | |
| assert_eq!(_which(&f, &b).unwrap(), f.bins[4].canonicalize().unwrap()); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which_relative() { | |
| let f = TestFixture::new(); | |
| assert_eq!( | |
| _which(&f, "b/bin").unwrap(), | |
| f.bins[3].canonicalize().unwrap() | |
| ); | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which_relative() { | |
| let f = TestFixture::new(); | |
| assert_eq!( | |
| _which(&f, "b/bin").unwrap(), | |
| f.bins[4].canonicalize().unwrap() | |
| ); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which_relative_extension() { | |
| // test_which_relative tests a relative path without an extension, | |
| // so test a relative path with an extension here. | |
| let f = TestFixture::new(); | |
| let b = Path::new("b/bin").with_extension(env::consts::EXE_EXTENSION); | |
| assert_eq!(_which(&f, &b).unwrap(), f.bins[3].canonicalize().unwrap()); | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which_relative_extension() { | |
| // test_which_relative tests a relative path without an extension, | |
| // so test a relative path with an extension here. | |
| let f = TestFixture::new(); | |
| let b = Path::new("b/bin").with_extension("cmd"); | |
| assert_eq!(_which(&f, &b).unwrap(), f.bins[5].canonicalize().unwrap()); | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which_relative_extension_case() { | |
| // Test that a relative path with an uppercase extension | |
| // is accepted. | |
| let f = TestFixture::new(); | |
| let b = Path::new("b/bin").with_extension("EXE"); | |
| assert_eq!(_which(&f, &b).unwrap(), f.bins[4].canonicalize().unwrap()); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which_relative_leading_dot() { | |
| let f = TestFixture::new(); | |
| assert_eq!( | |
| _which(&f, "./b/bin").unwrap(), | |
| f.bins[3].canonicalize().unwrap() | |
| ); | |
| } | |
| #[test] | |
| #[cfg(windows)] | |
| fn test_which_relative_leading_dot() { | |
| let f = TestFixture::new(); | |
| assert_eq!( | |
| _which(&f, "./b/bin").unwrap(), | |
| f.bins[4].canonicalize().unwrap() | |
| ); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which_non_executable() { | |
| // Shouldn't return non-executable files. | |
| let f = TestFixture::new(); | |
| f.touch("b/another", "").unwrap(); | |
| assert!(_which(&f, "another").is_err()); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which_absolute_non_executable() { | |
| // Shouldn't return non-executable files, even if given an absolute path. | |
| let f = TestFixture::new(); | |
| let b = f.touch("b/another", "").unwrap(); | |
| assert!(_which(&f, &b).is_err()); | |
| } | |
| #[test] | |
| #[cfg(unix)] | |
| fn test_which_relative_non_executable() { | |
| // Shouldn't return non-executable files. | |
| let f = TestFixture::new(); | |
| f.touch("b/another", "").unwrap(); | |
| assert!(_which(&f, "b/another").is_err()); | |
| } | |
| #[test] | |
| fn test_failure() { | |
| let f = TestFixture::new(); | |
| let run = || -> which::Result<PathBuf> { | |
| let p = _which(&f, "./b/bin")?; | |
| Ok(p.into_path_buf()) | |
| }; | |
| let _ = run(); | |
| } |