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, } const SUBDIRS: &[&str] = &["a", "b", "c"]; const BIN_NAME: &str = "bin"; #[cfg(unix)] fn mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result { 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 { 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 { 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 { touch(self.tempdir.path(), path, extension) } pub fn mk_bin(&self, path: &str, extension: &str) -> io::Result { mk_bin(self.tempdir.path(), path, extension) } } fn _which>(f: &TestFixture, path: T) -> which::Result { which::CanonicalPath::new_in(path, Some(f.paths.clone()), f.tempdir.path()) } fn _which_all<'a, T: AsRef + 'a>( f: &'a TestFixture, path: T, ) -> which::Result> + '_> { 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 = 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 = which::which_re_in(re, Some(f.paths)) .unwrap() .into_iter() .collect(); assert_eq!(result, Vec::::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::>(); let mut expected = f .bins .iter() .map(|p| p.canonicalize().unwrap()) .collect::>(); #[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 { let p = _which(&f, "./b/bin")?; Ok(p.into_path_buf()) }; let _ = run(); }