use super::*;
type Case = (
fn(&Env) -> PathBuf,
&'static [(&'static str, &'static str)],
&'static str,
);
fn run(cases: &[Case]) {
for &(fold, vars, expect) in cases {
let env = Env::from_pairs(vars.iter().copied());
assert_eq!(fold(&env), PathBuf::from(expect), "vars={vars:?}");
}
}
#[test]
fn xdg_folds_set_unset_and_missing_home() {
run(&[
(
Env::balls_state_root,
&[("XDG_STATE_HOME", "/x")],
"/x/balls",
),
(
Env::balls_state_root,
&[("HOME", "/h")],
"/h/.local/state/balls",
),
(Env::balls_state_root, &[], "/.local/state/balls"),
(
Env::balls_clones_dir,
&[("XDG_STATE_HOME", "/x")],
"/x/balls/clones",
),
(
Env::balls_clones_dir,
&[("HOME", "/h")],
"/h/.local/state/balls/clones",
),
(Env::yog_data_root, &[("XDG_DATA_HOME", "/d")], "/d/yog"),
(Env::yog_data_root, &[("HOME", "/h")], "/h/.local/share/yog"),
(Env::yog_data_root, &[], ".local/share/yog"),
(Env::yog_state_root, &[("XDG_STATE_HOME", "/x")], "/x/yog"),
(
Env::yog_state_root,
&[("HOME", "/h")],
"/h/.local/state/yog",
),
(
Env::yog_stage_root,
&[("XDG_STATE_HOME", "/x")],
"/x/yog/stage",
),
(
Env::yog_stage_root,
&[("HOME", "/h")],
"/h/.local/state/yog/stage",
),
(Env::home_dir, &[("HOME", "/h")], "/h"),
(Env::home_dir, &[], "/"),
]);
}
#[test]
fn litany_roots_home_collapse_set_empty_unset() {
run(&[
(
Env::litany_config_root,
&[("LITANY_HOME", "/L"), ("XDG_CONFIG_HOME", "/c")],
"/L",
),
(
Env::litany_data_root,
&[("LITANY_HOME", "/L"), ("XDG_DATA_HOME", "/d")],
"/L",
),
(
Env::litany_config_root,
&[("LITANY_HOME", ""), ("XDG_CONFIG_HOME", "/c")],
"/c/litany",
),
(
Env::litany_data_root,
&[("LITANY_HOME", ""), ("XDG_DATA_HOME", "/d")],
"/d/litany",
),
(
Env::litany_config_root,
&[("XDG_CONFIG_HOME", "/c")],
"/c/litany",
),
(
Env::litany_config_root,
&[("HOME", "/h")],
"/h/.config/litany",
),
(Env::litany_config_root, &[], ".config/litany"),
(
Env::litany_data_root,
&[("HOME", "/h")],
"/h/.local/share/litany",
),
(Env::litany_data_root, &[], ".local/share/litany"),
]);
}
#[test]
fn percent_decode_table() {
let cases = [
("home%2Fmark", "home/mark"), ("home%2fmark", "home/mark"), ("a%20b", "a b"), ("plain", "plain"), ("", ""), ("%GG", "%GG"), ("tail%", "tail%"), ("x%4", "x%4"), ("%41%42", "AB"), ];
for (input, expect) in cases {
assert_eq!(percent_decode(input), expect, "input={input:?}");
}
}
#[test]
fn user_reads_the_snapshot_and_empty_is_absent() {
assert_eq!(
Env::from_pairs([("USER", "orion")]).user().as_deref(),
Some("orion"),
);
assert_eq!(Env::from_pairs([("USER", "")]).user(), None);
assert_eq!(
Env::from_pairs(std::iter::empty::<(&str, &str)>()).user(),
None
);
}
#[test]
fn from_env_snapshots_process_env() {
let env = Env::from_env();
let _ = env.yog_state_root();
}
#[test]
fn the_clone_roots_are_the_worlds_and_the_hosts_or_the_one_they_collapse_to() {
let ambient = Env::from_pairs([
("HOME", "/h"),
("XDG_DATA_HOME", "/d"),
("XDG_STATE_HOME", "/s"),
]);
assert_eq!(
ambient.balls_clone_roots(),
vec![PathBuf::from("/s/balls/clones")],
"outside a world there is one bundle"
);
assert_eq!(
crate::world::compose(&ambient).balls_clone_roots(),
vec![
PathBuf::from("/d/yog/world/state/balls/clones"),
PathBuf::from("/s/balls/clones"),
],
"inside one, the world's own and the host's — world first, since a \
path present in both is one project and the first reading wins"
);
}
#[test]
fn the_host_state_home_is_the_ambient_reading_the_world_carried_in() {
let ambient = Env::from_pairs([("HOME", "/h"), ("XDG_STATE_HOME", "/s")]);
assert_eq!(ambient.host_state_home(), PathBuf::from("/s"));
assert_eq!(
Env::from_pairs([("HOME", "/h")]).host_state_home(),
PathBuf::from("/h/.local/state"),
"with no XDG_STATE_HOME it is balls' own fallback, off HOME"
);
let world = crate::world::compose(&ambient);
assert_ne!(world.host_state_home(), world.balls_state_home());
assert_eq!(world.host_state_home(), PathBuf::from("/s"));
}