mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-27 11:37:13 -06:00
Edit in PhotoCraft: restore the Windows/desktop-entry tests, test PATH lookup, sort AppImages by version
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
380d3017b0
commit
a656f99e47
@@ -146,12 +146,7 @@ fn find_photocraft() -> Option<Launcher> {
|
||||
// registers one on first run; its Exec a path or a name on the PATH), an AppImage where people
|
||||
// keep them, else the Flatpak
|
||||
let path_dirs: Vec<PathBuf> = env("PATH").into_iter().flat_map(|p| std::env::split_paths(&p).collect::<Vec<_>>()).collect();
|
||||
let on_path = |name: &Path| -> Option<PathBuf> {
|
||||
if name.is_absolute() || name.starts_with("/") {
|
||||
return Some(name.to_path_buf()).filter(|p| p.is_file());
|
||||
}
|
||||
path_dirs.iter().map(|d| d.join(name)).find(|p| p.is_file())
|
||||
};
|
||||
let on_path = |name: &Path| resolve_program(name, &path_dirs);
|
||||
let from_entry = || {
|
||||
desktop_entry_dirs(env).into_iter().find_map(|d| {
|
||||
let text = std::fs::read_to_string(d.join(format!("{PHOTOCRAFT_ID}.desktop"))).ok()?;
|
||||
@@ -164,8 +159,8 @@ fn find_photocraft() -> Option<Launcher> {
|
||||
dirs.iter().find_map(|d| {
|
||||
let mut found: Vec<PathBuf> =
|
||||
std::fs::read_dir(d).ok()?.flatten().map(|e| e.path()).filter(|p| is_photocraft_appimage(p) && p.is_file()).collect();
|
||||
// the newest version by name
|
||||
found.sort();
|
||||
// the newest version by name (1.10 after 1.9)
|
||||
found.sort_by_key(|p| version_key(&p.file_name().map(|n| n.to_string_lossy().to_lowercase()).unwrap_or_default()));
|
||||
found.pop()
|
||||
})
|
||||
};
|
||||
@@ -239,6 +234,31 @@ fn desktop_exec(entry: &str) -> Option<PathBuf> {
|
||||
(!program.is_empty()).then(|| PathBuf::from(program))
|
||||
}
|
||||
|
||||
/// A program from a desktop entry or the PATH: a path as it is (if it is a file), a bare name
|
||||
/// looked for in `dirs`. A leading `/` counts as absolute on every platform (desktop entries are
|
||||
/// a POSIX format; `Path::is_absolute` wants a drive letter on Windows, where the tests run too).
|
||||
fn resolve_program(name: &Path, dirs: &[PathBuf]) -> Option<PathBuf> {
|
||||
if name.is_absolute() || name.starts_with("/") {
|
||||
return Some(name.to_path_buf()).filter(|p| p.is_file());
|
||||
}
|
||||
dirs.iter().map(|d| d.join(name)).find(|p| p.is_file())
|
||||
}
|
||||
|
||||
/// A sort key that orders the digit runs in a name by value (`1.9` before `1.10`).
|
||||
fn version_key(name: &str) -> Vec<(u64, String)> {
|
||||
let mut out = Vec::new();
|
||||
let mut rest = name;
|
||||
while !rest.is_empty() {
|
||||
let digits = rest.len() - rest.trim_start_matches(|c: char| c.is_ascii_digit()).len();
|
||||
let (num, tail) = rest.split_at(digits);
|
||||
let text_len = tail.find(|c: char| c.is_ascii_digit()).unwrap_or(tail.len());
|
||||
let (text, next) = tail.split_at(text_len);
|
||||
out.push((num.parse().unwrap_or(u64::MAX), text.to_string()));
|
||||
rest = next;
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// An AppImage of PhotoCraft by its file name (`PhotoCraft-1.2-x86_64.AppImage`, any case).
|
||||
fn is_photocraft_appimage(p: &Path) -> bool {
|
||||
let name = p.file_name().map(|n| n.to_string_lossy().to_lowercase()).unwrap_or_default();
|
||||
@@ -284,6 +304,68 @@ mod tests {
|
||||
assert!(all_command(Launcher::Default, &paths).is_none(), "the system default opens them one by one");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn windows_lookup_prefers_the_recorded_install_folder() {
|
||||
let env = |k: &str| match k {
|
||||
"ProgramFiles" => Some(r"C:\Program Files".to_string()),
|
||||
"LOCALAPPDATA" => Some(r"C:\Users\Simon\AppData\Local".to_string()),
|
||||
_ => None,
|
||||
};
|
||||
let c = windows_candidates(Some(r"D:\Apps\PhotoCraft\".into()), Some(r#""C:\Program Files\PhotoCraft\photocraft.exe""#.into()), env);
|
||||
let c: Vec<String> = c.iter().map(|p| p.to_string_lossy().replace('/', "\\")).collect();
|
||||
assert!(c[0].starts_with(r"D:\Apps\PhotoCraft") && c[0].ends_with("photocraft.exe"), "{c:?}");
|
||||
assert_eq!(c[1], r"C:\Program Files\PhotoCraft\photocraft.exe", "App Paths, quotes dropped");
|
||||
assert!(c[2].starts_with(r"C:\Program Files") && c[2].ends_with("photocraft.exe"), "{c:?}");
|
||||
assert!(c[3].contains("Programs") && c[3].ends_with("photocraft.exe"), "{c:?}");
|
||||
assert_eq!(c.len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reg_query_output_is_read() {
|
||||
let out = "\r\nHKEY_LOCAL_MACHINE\\Software\\PhotoCraft\r\n InstallDir REG_SZ C:\\Program Files\\PhotoCraft\\\r\n\r\n";
|
||||
assert_eq!(reg_value(out).as_deref(), Some(r"C:\Program Files\PhotoCraft\"));
|
||||
let default = "\r\nHKEY_LOCAL_MACHINE\\...\\photocraft.exe\r\n (Default) REG_SZ C:\\Program Files\\PhotoCraft\\photocraft.exe\r\n";
|
||||
assert_eq!(reg_value(default).as_deref(), Some(r"C:\Program Files\PhotoCraft\photocraft.exe"));
|
||||
assert_eq!(reg_value("ERROR: The system was unable to find the specified registry key or value."), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desktop_entries_give_their_program() {
|
||||
let appimage = "[Desktop Entry]\nName=PhotoCraft\nExec=\"/home/simon/Apps/photocraft-1.2-linux-x86_64.AppImage\" %F\n";
|
||||
assert_eq!(desktop_exec(appimage), Some(PathBuf::from("/home/simon/Apps/photocraft-1.2-linux-x86_64.AppImage")));
|
||||
assert_eq!(desktop_exec("Exec=/opt/photocraft/photocraft %F\n"), Some(PathBuf::from("/opt/photocraft/photocraft")));
|
||||
assert_eq!(desktop_exec("Exec=photocraft %F\nTryExec=photocraft\n"), Some(PathBuf::from("photocraft")), "a bare name, for a PATH lookup");
|
||||
assert_eq!(desktop_exec("[Desktop Entry]\n"), None);
|
||||
let dirs = desktop_entry_dirs(|k| (k == "HOME").then(|| "/home/simon".to_string()));
|
||||
assert_eq!(dirs[0], PathBuf::from("/home/simon/.local/share/applications"));
|
||||
assert!(dirs.contains(&PathBuf::from("/usr/share/applications")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_bare_exec_name_is_found_on_the_path() {
|
||||
let a = std::env::temp_dir().join(format!("lc-editors-a-{}", std::process::id()));
|
||||
let b = std::env::temp_dir().join(format!("lc-editors-b-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&a).unwrap();
|
||||
std::fs::create_dir_all(&b).unwrap();
|
||||
std::fs::write(b.join("photocraft"), b"").unwrap();
|
||||
let dirs = vec![a.clone(), b.clone()];
|
||||
assert_eq!(resolve_program(Path::new("photocraft"), &dirs), Some(b.join("photocraft")));
|
||||
assert_eq!(resolve_program(Path::new("nothere"), &dirs), None);
|
||||
let abs = b.join("photocraft");
|
||||
assert_eq!(resolve_program(&abs, &[]), Some(abs.clone()), "a path needs no PATH");
|
||||
assert_eq!(resolve_program(&b.join("missing"), &dirs), None);
|
||||
let _ = std::fs::remove_dir_all(&a);
|
||||
let _ = std::fs::remove_dir_all(&b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn appimage_versions_sort_numerically() {
|
||||
let mut names = ["photocraft-1.10-x86_64.appimage", "photocraft-1.9-x86_64.appimage", "photocraft-1.2-x86_64.appimage"];
|
||||
names.sort_by_key(|n| version_key(n));
|
||||
assert_eq!(names[2], "photocraft-1.10-x86_64.appimage");
|
||||
assert_eq!(names[0], "photocraft-1.2-x86_64.appimage");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_failed_open_is_an_error() {
|
||||
assert!(open_result(true, "").is_ok());
|
||||
|
||||
Reference in New Issue
Block a user