Skip to content

Commit

Permalink
Add tests for internal implementation of which()
Browse files Browse the repository at this point in the history
  • Loading branch information
0xzhzh committed Dec 1, 2024
1 parent 0c6f5e8 commit 389b2ae
Showing 1 changed file with 145 additions and 17 deletions.
162 changes: 145 additions & 17 deletions tests/which_exec.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,46 @@
use super::*;

fn make_path() -> TempDir {
let tmp = temptree! {
"hello.exe": "#!/usr/bin/env bash\necho hello\n",
};
trait TempDirExt {
fn executable(self, file: impl AsRef<Path>) -> Self;
}

#[cfg(not(windows))]
{
let exe = tmp.path().join("hello.exe");
let perms = std::os::unix::fs::PermissionsExt::from_mode(0o755);
fs::set_permissions(exe, perms).unwrap();
}
impl TempDirExt for TempDir {
fn executable(self, file: impl AsRef<Path>) -> Self {
let file = self.path().join(file.as_ref());

// Make sure it exists first, as a sanity check.
assert!(
file.exists(),
"executable file does not exist: {}",
file.display()
);

// Windows uses file extensions to determine whether a file is executable.
// Other systems don't care. To keep these tests cross-platform, just make
// sure all executables end with ".exe" suffix.
assert!(
file.extension() == Some("exe".as_ref()),
"executable file does not end with .exe: {}",
file.display()
);

#[cfg(not(windows))]
{
let perms = std::os::unix::fs::PermissionsExt::from_mode(0o755);
fs::set_permissions(file, perms).unwrap();
}

tmp
self
}
}

#[test]
fn finds_executable() {
let tmp = make_path();
let mut path = env::current_dir().unwrap();
path.push("bin");
let tmp = temptree! {
"hello.exe": "#!/usr/bin/env bash\necho hello\n",
}
.executable("hello.exe");

Test::new()
.justfile(r#"p := which("hello.exe")"#)
.env("PATH", tmp.path().to_str().unwrap())
Expand All @@ -30,13 +51,120 @@ fn finds_executable() {

#[test]
fn prints_empty_string_for_missing_executable() {
let tmp = make_path();
let mut path = env::current_dir().unwrap();
path.push("bin");
let tmp = temptree! {
"hello.exe": "#!/usr/bin/env bash\necho hello\n",
}
.executable("hello.exe");

Test::new()
.justfile(r#"p := which("goodbye.exe")"#)
.env("PATH", tmp.path().to_str().unwrap())
.args(["--evaluate", "p"])
.stdout("")
.run();
}

#[test]
fn skips_non_executable_files() {
let tmp = temptree! {
"hello.exe": "#!/usr/bin/env bash\necho hello\n",
"hi": "just some regular file",
}
.executable("hello.exe");

Test::new()
.justfile(r#"p := which("hi")"#)
.env("PATH", tmp.path().to_str().unwrap())
.args(["--evaluate", "p"])
.stdout("")
.run();
}

#[test]
fn supports_multiple_paths() {
let tmp1 = temptree! {
"hello1.exe": "#!/usr/bin/env bash\necho hello\n",
}
.executable("hello1.exe");

let tmp2 = temptree! {
"hello2.exe": "#!/usr/bin/env bash\necho hello\n",
}
.executable("hello2.exe");

let path =
env::join_paths([tmp1.path().to_str().unwrap(), tmp2.path().to_str().unwrap()]).unwrap();

Test::new()
.justfile(r#"p := which("hello1.exe")"#)
.env("PATH", path.to_str().unwrap())
.args(["--evaluate", "p"])
.stdout(format!("{}", tmp1.path().join("hello1.exe").display()))
.run();

Test::new()
.justfile(r#"p := which("hello2.exe")"#)
.env("PATH", path.to_str().unwrap())
.args(["--evaluate", "p"])
.stdout(format!("{}", tmp2.path().join("hello2.exe").display()))
.run();
}

#[test]
fn supports_shadowed_executables() {
let tmp1 = temptree! {
"shadowed.exe": "#!/usr/bin/env bash\necho hello\n",
}
.executable("shadowed.exe");

let tmp2 = temptree! {
"shadowed.exe": "#!/usr/bin/env bash\necho hello\n",
}
.executable("shadowed.exe");

// which should never resolve to this directory, no matter where or how many
// times it appears in PATH, because the "shadowed" file is not executable.
let dummy = if cfg!(windows) {
temptree! {
"shadowed": "#!/usr/bin/env bash\necho hello\n",
}
} else {
temptree! {
"shadowed.exe": "#!/usr/bin/env bash\necho hello\n",
}
};

// This PATH should give priority to tmp1/shadowed.exe
let tmp1_path = env::join_paths([
dummy.path().to_str().unwrap(),
tmp1.path().to_str().unwrap(),
dummy.path().to_str().unwrap(),
tmp2.path().to_str().unwrap(),
dummy.path().to_str().unwrap(),
])
.unwrap();

// This PATH should give priority to tmp2/shadowed.exe
let tmp2_path = env::join_paths([
dummy.path().to_str().unwrap(),
tmp2.path().to_str().unwrap(),
dummy.path().to_str().unwrap(),
tmp1.path().to_str().unwrap(),
dummy.path().to_str().unwrap(),
])
.unwrap();

Test::new()
.justfile(r#"p := which("shadowed.exe")"#)
.env("PATH", tmp1_path.to_str().unwrap())
.args(["--evaluate", "p"])
.stdout(format!("{}", tmp1.path().join("shadowed.exe").display()))
.run();

Test::new()
.justfile(r#"p := which("shadowed.exe")"#)
.env("PATH", tmp2_path.to_str().unwrap())
.args(["--evaluate", "p"])
.stdout(format!("{}", tmp2.path().join("shadowed.exe").display()))
.run();
}

0 comments on commit 389b2ae

Please sign in to comment.