first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
use std::ffi::OsStr;
|
||||
#[cfg(unix)]
|
||||
use std::ffi::OsString;
|
||||
#[cfg(unix)]
|
||||
use std::fs;
|
||||
#[cfg(unix)]
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{known_bare_name, resolve_binary_in_wsl_safe_path};
|
||||
|
||||
#[cfg(unix)]
|
||||
fn make_executable(path: &std::path::Path) {
|
||||
use std::os::unix::fs::PermissionsExt as _;
|
||||
let mut perms = fs::metadata(path).unwrap().permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(path, perms).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn write_exec(path: &std::path::Path) {
|
||||
fs::write(path, b"#!/bin/sh\nexit 0\n").unwrap();
|
||||
make_executable(path);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn join(parts: &[PathBuf]) -> OsString {
|
||||
std::env::join_paths(parts).unwrap()
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn picks_first_linux_path_when_wsl() {
|
||||
let linux_dir = tempfile::tempdir().unwrap();
|
||||
write_exec(&linux_dir.path().join("git"));
|
||||
|
||||
// `/mnt/c/...` paths in the synthetic PATH that don't exist on
|
||||
// disk simulate the WSL-with-Windows-git layout: the resolver
|
||||
// should skip them on the prefix and never stat them.
|
||||
let path_env = join(&[
|
||||
PathBuf::from("/mnt/c/Program Files/Git/cmd"),
|
||||
linux_dir.path().to_path_buf(),
|
||||
]);
|
||||
|
||||
let resolved =
|
||||
resolve_binary_in_wsl_safe_path("git", Some(path_env.as_os_str()), true).unwrap();
|
||||
assert_eq!(resolved, linux_dir.path().join("git"));
|
||||
assert!(!resolved.starts_with("/mnt"));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn passes_through_first_match_when_not_wsl() {
|
||||
// When not WSL, `/mnt/...` is just another directory; the resolver
|
||||
// should pick the first dir on PATH that contains an exec match.
|
||||
let mnt_dir = tempfile::tempdir().unwrap();
|
||||
write_exec(&mnt_dir.path().join("git"));
|
||||
let other_dir = tempfile::tempdir().unwrap();
|
||||
write_exec(&other_dir.path().join("git"));
|
||||
|
||||
let path_env = join(&[mnt_dir.path().to_path_buf(), other_dir.path().to_path_buf()]);
|
||||
|
||||
let resolved =
|
||||
resolve_binary_in_wsl_safe_path("git", Some(path_env.as_os_str()), false).unwrap();
|
||||
assert_eq!(resolved, mnt_dir.path().join("git"));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn falls_back_to_none_when_only_mnt_has_git() {
|
||||
let path_env = join(&[
|
||||
PathBuf::from("/mnt/c/Program Files/Git/cmd"),
|
||||
PathBuf::from("/mnt/c/Windows/System32"),
|
||||
]);
|
||||
assert!(resolve_binary_in_wsl_safe_path("git", Some(path_env.as_os_str()), true).is_none());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn picks_user_local_bin() {
|
||||
let bin_dir = tempfile::tempdir().unwrap();
|
||||
let empty_dir = bin_dir.path().join("empty");
|
||||
fs::create_dir_all(&empty_dir).unwrap();
|
||||
let local_bin = bin_dir.path().join("home/.local/bin");
|
||||
fs::create_dir_all(&local_bin).unwrap();
|
||||
write_exec(&local_bin.join("git"));
|
||||
|
||||
// PATH order: an empty dir, then a `/mnt/...` candidate that
|
||||
// should be skipped on WSL, then the directory with the real
|
||||
// exec. The resolver must walk past the first two and land on
|
||||
// the third.
|
||||
let path_env = join(&[
|
||||
empty_dir,
|
||||
PathBuf::from("/mnt/c/Program Files/Git/cmd"),
|
||||
local_bin.clone(),
|
||||
]);
|
||||
let resolved =
|
||||
resolve_binary_in_wsl_safe_path("git", Some(path_env.as_os_str()), true).unwrap();
|
||||
assert_eq!(resolved, local_bin.join("git"));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn follows_symlinks() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let real = dir.path().join("git-wrapper");
|
||||
write_exec(&real);
|
||||
let link = dir.path().join("git");
|
||||
std::os::unix::fs::symlink(&real, &link).unwrap();
|
||||
|
||||
let path_env = join(&[dir.path().to_path_buf()]);
|
||||
let resolved =
|
||||
resolve_binary_in_wsl_safe_path("git", Some(path_env.as_os_str()), true).unwrap();
|
||||
assert_eq!(resolved, link);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn skips_non_executable() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
fs::write(dir.path().join("git"), b"not exec").unwrap();
|
||||
// Intentionally do NOT chmod +x.
|
||||
|
||||
let path_env = join(&[dir.path().to_path_buf()]);
|
||||
assert!(resolve_binary_in_wsl_safe_path("git", Some(path_env.as_os_str()), true).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handles_empty_path_env() {
|
||||
assert!(resolve_binary_in_wsl_safe_path("git", None, true).is_none());
|
||||
assert!(resolve_binary_in_wsl_safe_path("git", None, false).is_none());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn handles_non_utf8_path_components() {
|
||||
use std::os::unix::ffi::OsStringExt as _;
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
write_exec(&dir.path().join("git"));
|
||||
|
||||
// Build a PATH whose first component is a non-UTF-8 byte sequence,
|
||||
// followed by a real directory. The resolver must walk past the
|
||||
// garbage entry without panicking and find the valid one.
|
||||
let mut bytes = b"/\xff\xfe/bad:".to_vec();
|
||||
bytes.extend_from_slice(dir.path().as_os_str().as_encoded_bytes());
|
||||
let path_env = OsString::from_vec(bytes);
|
||||
|
||||
let resolved =
|
||||
resolve_binary_in_wsl_safe_path("git", Some(path_env.as_os_str()), true).unwrap();
|
||||
assert_eq!(resolved, dir.path().join("git"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_bare_name_recognizes_git_and_gh() {
|
||||
assert_eq!(known_bare_name(OsStr::new("git")), Some("git"));
|
||||
assert_eq!(known_bare_name(OsStr::new("gh")), Some("gh"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_bare_name_skips_paths() {
|
||||
assert_eq!(known_bare_name(OsStr::new("/usr/bin/git")), None);
|
||||
assert_eq!(known_bare_name(OsStr::new("./git")), None);
|
||||
assert_eq!(known_bare_name(OsStr::new("bin/git")), None);
|
||||
#[cfg(windows)]
|
||||
assert_eq!(known_bare_name(OsStr::new("C:\\git\\git.exe")), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn known_bare_name_skips_unknowns() {
|
||||
assert_eq!(known_bare_name(OsStr::new("ls")), None);
|
||||
assert_eq!(known_bare_name(OsStr::new("python")), None);
|
||||
assert_eq!(known_bare_name(OsStr::new("")), None);
|
||||
}
|
||||
Reference in New Issue
Block a user