use proptest::prelude::*;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
fn create_temp_dir(label: &str) -> PathBuf {
let id = COUNTER.fetch_add(1, Ordering::Relaxed);
let dir = std::env::temp_dir().join(format!(
"gpui_aux_exec_test_{label}_{}_{id}",
std::process::id()
));
let _ = fs::remove_dir_all(&dir);
fs::create_dir_all(&dir).expect("failed to create temp dir");
dir
}
fn resolve_auxiliary_executable(
name: &str,
app_dir: &Path,
extra_dirs: &[PathBuf],
) -> anyhow::Result<PathBuf> {
let candidate = app_dir.join(name);
if candidate.exists() {
return Ok(candidate);
}
#[cfg(target_os = "windows")]
if !name.ends_with(".exe") {
let candidate_exe = app_dir.join(format!("{name}.exe"));
if candidate_exe.exists() {
return Ok(candidate_exe);
}
}
for dir in extra_dirs {
let candidate = dir.join(name);
if candidate.exists() {
return Ok(candidate);
}
#[cfg(target_os = "windows")]
if !name.ends_with(".exe") {
let candidate_exe = dir.join(format!("{name}.exe"));
if candidate_exe.exists() {
return Ok(candidate_exe);
}
}
}
anyhow::bail!("could not find auxiliary executable '{name}'")
}
fn binary_name_strategy() -> impl Strategy<Value = String> {
"[a-zA-Z][a-zA-Z0-9_-]{0,29}"
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn auxiliary_executable_resolves_to_existing_file(name in binary_name_strategy()) {
let app_dir = create_temp_dir("app");
let binary_path = app_dir.join(&name);
fs::write(&binary_path, b"fake-executable-content").unwrap();
let result = resolve_auxiliary_executable(&name, &app_dir, &[]);
let _ = fs::remove_dir_all(&app_dir);
prop_assert!(result.is_ok(), "resolution must succeed for a binary that exists in the app directory");
let resolved = result.unwrap();
prop_assert_eq!(
resolved, binary_path,
"resolved path must point to the binary in the app directory"
);
}
#[test]
fn auxiliary_executable_resolves_via_path_fallback(name in binary_name_strategy()) {
let app_dir = create_temp_dir("app_empty");
let path_dir = create_temp_dir("path");
let binary_path = path_dir.join(&name);
fs::write(&binary_path, b"fake-executable-content").unwrap();
let result = resolve_auxiliary_executable(&name, &app_dir, &[path_dir.clone()]);
let _ = fs::remove_dir_all(&app_dir);
let _ = fs::remove_dir_all(&path_dir);
prop_assert!(result.is_ok(), "resolution must succeed when binary exists in a PATH directory");
prop_assert_eq!(
result.unwrap(), binary_path,
"resolved path must point to the binary in the PATH directory"
);
}
#[test]
fn auxiliary_executable_errors_for_missing_binary(name in binary_name_strategy()) {
let app_dir = create_temp_dir("app_miss");
let result = resolve_auxiliary_executable(&name, &app_dir, &[]);
let _ = fs::remove_dir_all(&app_dir);
prop_assert!(result.is_err(), "resolution must fail when the binary does not exist anywhere");
}
#[test]
fn auxiliary_executable_prefers_app_dir_over_path(name in binary_name_strategy()) {
let app_dir = create_temp_dir("app_pref");
let path_dir = create_temp_dir("path_pref");
let app_binary = app_dir.join(&name);
let path_binary = path_dir.join(&name);
fs::write(&app_binary, b"app-version").unwrap();
fs::write(&path_binary, b"path-version").unwrap();
let result = resolve_auxiliary_executable(&name, &app_dir, &[path_dir.clone()]);
let _ = fs::remove_dir_all(&app_dir);
let _ = fs::remove_dir_all(&path_dir);
prop_assert!(result.is_ok());
let resolved = result.unwrap();
prop_assert!(
resolved.starts_with(&app_dir),
"app directory must take precedence over PATH directories"
);
}
}