use camino::{Utf8Path, Utf8PathBuf};
use color_eyre::eyre::{Result, eyre};
use rstest::rstest;
use super::fixture::temp_root;
use crate::extensions::tree::{Destination, InstallTree};
const BYTES: &[u8] = b"the planned contents";
fn tree() -> Result<(tempfile::TempDir, InstallTree, Utf8PathBuf)> {
let (temp, root) = temp_root()?;
let tree = InstallTree::open(&root)
.map_err(|err| eyre!("cannot open the installation tree {root}: {err}"))?;
Ok((temp, tree, root))
}
#[rstest]
fn an_absent_destination_is_named_absent() {
let (_root, tree, _path) = tree().expect("arrange the installation tree");
let found = tree.inspect_destination(Utf8Path::new("lib/ext.so"), BYTES);
assert!(
matches!(found, Destination::Absent),
"nothing is there, and that is the ordinary first install"
);
assert_eq!(
found.rewrite_reason().as_deref(),
Some("nothing is there"),
"the reason reaches the log"
);
}
#[rstest]
fn a_matching_file_is_named_identical() {
let (_root, tree, path) = tree().expect("arrange the installation tree");
std::fs::write(path.join("ext.so"), BYTES).expect("plant the identical file");
let found = tree.inspect_destination(Utf8Path::new("ext.so"), BYTES);
assert!(
matches!(found, Destination::Identical(_)),
"a regular file holding exactly these bytes is reusable"
);
assert!(
found.rewrite_reason().is_none(),
"nothing is rewritten, so there is no reason to report"
);
}
#[rstest]
fn a_file_holding_other_bytes_is_named_different() {
let (_root, tree, path) = tree().expect("arrange the installation tree");
std::fs::write(path.join("ext.so"), b"an older build").expect("plant the stale file");
let found = tree.inspect_destination(Utf8Path::new("ext.so"), BYTES);
assert!(
matches!(found, Destination::Different),
"a regular file holding other bytes is replaced, and says so"
);
}
#[cfg(unix)]
#[rstest]
fn a_directory_at_the_destination_is_named_not_regular() {
let (_root, tree, path) = tree().expect("arrange the installation tree");
std::fs::create_dir(path.join("ext.so")).expect("plant a directory");
let found = tree.inspect_destination(Utf8Path::new("ext.so"), BYTES);
assert!(
matches!(found, Destination::NotRegular),
"something other than a regular file is not a differing file"
);
}
#[cfg(windows)]
#[rstest]
fn a_directory_at_the_destination_is_refused_by_the_open() {
let (_root, tree, path) = tree().expect("arrange the installation tree");
std::fs::create_dir(path.join("ext.so")).expect("plant a directory");
let found = tree.inspect_destination(Utf8Path::new("ext.so"), BYTES);
assert!(
matches!(found, Destination::Unreadable(_)),
"a directory cannot be opened for reading here, so the open refuses it"
);
assert!(
found
.rewrite_reason()
.is_some_and(|reason| reason.starts_with("what is there cannot be read")),
"the operator is told the destination could not be read"
);
}
#[cfg(unix)]
#[rstest]
fn a_symlinked_destination_is_named_unreadable() {
let (_root, tree, path) = tree().expect("arrange the installation tree");
let outside = path.join("elsewhere");
std::fs::write(&outside, BYTES).expect("write the link's target");
std::os::unix::fs::symlink(outside.as_std_path(), path.join("ext.so").as_std_path())
.expect("plant the symlink");
let found = tree.inspect_destination(Utf8Path::new("ext.so"), BYTES);
assert!(
matches!(found, Destination::Unreadable(_)),
"a symlink is refused by the open even when its target holds the bytes"
);
assert!(
found
.rewrite_reason()
.is_some_and(|reason| reason.starts_with("what is there cannot be read")),
"the operator is told the destination could not be read"
);
}