Skip to content

Commit 130b29f

Browse files
authored
test: add pet-homebrew locator coverage (#394)
Adds focused `pet-homebrew` unit tests as a small follow-up slice from the broader coverage plan. - Cover Homebrew locator metadata and Linuxbrew identification - Cover non-Homebrew, virtualenv, and Conda rejection paths - Cover Homebrew path classification and `HOMEBREW_PREFIX` bin discovery - Keep this PR scoped to `pet-homebrew` only Fixes #393 Part of #389
1 parent 89ed290 commit 130b29f

File tree

5 files changed

+196
-0
lines changed

5 files changed

+196
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pet-homebrew/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ pet-core = { path = "../pet-core" }
1919
log = "0.4.21"
2020
regex = "1.10.4"
2121
rayon = "1.11.0"
22+
23+
[dev-dependencies]
24+
tempfile = "3.10"

crates/pet-homebrew/src/environment_locations.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,47 @@ pub fn get_homebrew_prefix_bin(env_vars: &EnvVariables) -> Vec<PathBuf> {
4848

4949
homebrew_prefixes
5050
}
51+
52+
#[cfg(test)]
53+
mod tests {
54+
use super::*;
55+
use std::fs;
56+
use tempfile::tempdir;
57+
58+
#[test]
59+
fn homebrew_prefix_bin_uses_existing_homebrew_prefix_env_var() {
60+
let homebrew_prefix = tempdir().unwrap();
61+
let homebrew_bin = homebrew_prefix.path().join("bin");
62+
fs::create_dir_all(&homebrew_bin).unwrap();
63+
let env_vars = EnvVariables {
64+
home: None,
65+
root: None,
66+
path: None,
67+
homebrew_prefix: Some(homebrew_prefix.path().to_string_lossy().to_string()),
68+
known_global_search_locations: vec![],
69+
};
70+
71+
let prefix_bins = get_homebrew_prefix_bin(&env_vars);
72+
73+
assert!(prefix_bins.contains(&homebrew_bin));
74+
}
75+
76+
#[test]
77+
fn homebrew_prefix_bin_ignores_missing_homebrew_prefix_env_var() {
78+
let homebrew_prefix_parent = tempdir().unwrap();
79+
let missing_homebrew_prefix = homebrew_prefix_parent.path().join("missing-prefix");
80+
let env_vars = EnvVariables {
81+
home: None,
82+
root: None,
83+
path: None,
84+
homebrew_prefix: Some(missing_homebrew_prefix.to_string_lossy().to_string()),
85+
known_global_search_locations: vec![],
86+
};
87+
88+
let prefix_bins = get_homebrew_prefix_bin(&env_vars);
89+
90+
assert!(!prefix_bins
91+
.iter()
92+
.any(|path| path == &missing_homebrew_prefix.join("bin")));
93+
}
94+
}

crates/pet-homebrew/src/lib.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,115 @@ impl Locator for Homebrew {
170170
});
171171
}
172172
}
173+
174+
#[cfg(all(test, unix))]
175+
mod tests {
176+
use super::*;
177+
use tempfile::tempdir;
178+
179+
struct TestEnvironment {
180+
homebrew_prefix: Option<String>,
181+
}
182+
183+
impl Environment for TestEnvironment {
184+
fn get_user_home(&self) -> Option<PathBuf> {
185+
None
186+
}
187+
188+
fn get_root(&self) -> Option<PathBuf> {
189+
None
190+
}
191+
192+
fn get_env_var(&self, key: String) -> Option<String> {
193+
if key == "HOMEBREW_PREFIX" {
194+
self.homebrew_prefix.clone()
195+
} else {
196+
None
197+
}
198+
}
199+
200+
fn get_know_global_search_locations(&self) -> Vec<PathBuf> {
201+
vec![]
202+
}
203+
}
204+
205+
#[test]
206+
fn homebrew_locator_reports_kind_and_supported_category() {
207+
let locator = Homebrew::from(&TestEnvironment {
208+
homebrew_prefix: None,
209+
});
210+
211+
assert_eq!(locator.get_kind(), LocatorKind::Homebrew);
212+
assert_eq!(
213+
locator.supported_categories(),
214+
vec![PythonEnvironmentKind::Homebrew]
215+
);
216+
}
217+
218+
#[test]
219+
fn try_from_identifies_linuxbrew_python_executable() {
220+
let locator = Homebrew::from(&TestEnvironment {
221+
homebrew_prefix: None,
222+
});
223+
let env = PythonEnv::new(
224+
PathBuf::from("/home/linuxbrew/.linuxbrew/Cellar/python@3.12/3.12.4/bin/python3.12"),
225+
None,
226+
None,
227+
);
228+
229+
let homebrew_env = locator.try_from(&env).unwrap();
230+
231+
assert_eq!(homebrew_env.kind, Some(PythonEnvironmentKind::Homebrew));
232+
assert_eq!(
233+
homebrew_env.executable,
234+
Some(PathBuf::from("/home/linuxbrew/.linuxbrew/bin/python3.12"))
235+
);
236+
assert_eq!(homebrew_env.version, Some("3.12.4".to_string()));
237+
assert_eq!(homebrew_env.prefix, None);
238+
assert!(homebrew_env
239+
.symlinks
240+
.as_ref()
241+
.unwrap()
242+
.contains(&PathBuf::from(
243+
"/home/linuxbrew/.linuxbrew/Cellar/python@3.12/3.12.4/bin/python3.12"
244+
)));
245+
}
246+
247+
#[test]
248+
fn try_from_rejects_non_homebrew_python() {
249+
let locator = Homebrew::from(&TestEnvironment {
250+
homebrew_prefix: None,
251+
});
252+
let env = PythonEnv::new(PathBuf::from("/usr/bin/python3.12"), None, None);
253+
254+
assert!(locator.try_from(&env).is_none());
255+
}
256+
257+
#[test]
258+
fn try_from_rejects_virtualenv_and_conda_prefixes() {
259+
let locator = Homebrew::from(&TestEnvironment {
260+
homebrew_prefix: None,
261+
});
262+
263+
let venv_root = tempdir().unwrap();
264+
let venv_bin = venv_root.path().join("bin");
265+
fs::create_dir_all(&venv_bin).unwrap();
266+
fs::write(venv_bin.join("activate"), b"").unwrap();
267+
let venv_executable = venv_bin.join("python3.12");
268+
fs::write(&venv_executable, b"").unwrap();
269+
let venv = PythonEnv::new(venv_executable, Some(venv_root.path().to_path_buf()), None);
270+
assert!(locator.try_from(&venv).is_none());
271+
272+
let conda_root = tempdir().unwrap();
273+
fs::create_dir_all(conda_root.path().join("conda-meta")).unwrap();
274+
let conda_executable = conda_root.path().join("bin").join("python3.12");
275+
fs::create_dir_all(conda_executable.parent().unwrap()).unwrap();
276+
fs::write(&conda_executable, b"").unwrap();
277+
let conda = PythonEnv::new(
278+
conda_executable,
279+
Some(conda_root.path().to_path_buf()),
280+
None,
281+
);
282+
assert!(locator.try_from(&conda).is_none());
283+
}
284+
}

crates/pet-homebrew/src/sym_links.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,39 @@ pub fn get_known_symlinks_impl(
243243
vec![]
244244
}
245245
}
246+
247+
#[cfg(test)]
248+
mod tests {
249+
use super::*;
250+
251+
#[test]
252+
fn homebrew_python_paths_are_recognized_across_supported_prefixes() {
253+
assert!(is_homebrew_python(Path::new(
254+
"/opt/homebrew/Cellar/python@3.12/3.12.4/bin/python3.12"
255+
)));
256+
assert!(is_homebrew_python(Path::new(
257+
"/usr/local/Cellar/python@3.11/3.11.9/bin/python3.11"
258+
)));
259+
assert!(is_homebrew_python(Path::new(
260+
"/home/linuxbrew/.linuxbrew/Cellar/python@3.12/3.12.4/bin/python3.12"
261+
)));
262+
assert!(!is_homebrew_python(Path::new("/usr/bin/python3.12")));
263+
}
264+
265+
#[test]
266+
fn known_symlink_templates_include_resolved_executable_for_linuxbrew() {
267+
let resolved_exe =
268+
PathBuf::from("/home/linuxbrew/.linuxbrew/Cellar/python@3.12/3.12.4/bin/python3.12");
269+
let symlinks = get_known_symlinks_impl(&resolved_exe, &"3.12.4".to_string());
270+
271+
assert!(symlinks.contains(&resolved_exe));
272+
}
273+
274+
#[test]
275+
fn known_symlink_templates_return_empty_for_unrecognized_paths() {
276+
assert!(
277+
get_known_symlinks_impl(Path::new("/usr/bin/python3.12"), &"3.12.4".to_string())
278+
.is_empty()
279+
);
280+
}
281+
}

0 commit comments

Comments
 (0)