blob: 32a1a289d698bdcf3dba6d7cf682a2eea66a5310 [file] [log] [blame]
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();
}