use super::{
COUNT_VARIABLE, FORWARD_TARGET_VARIABLE, child_environment_overlay, environment_with_overlay,
lockfile_pid_overlay,
};
use std::ffi::OsString;
use std::path::Path;
use std::process::Command;
pub(super) fn environment(pairs: &[(&str, &str)]) -> Vec<(OsString, OsString)> {
let mut result: Vec<(OsString, OsString)> = Vec::<(OsString, OsString)>::new();
for (name, value) in pairs {
result.push((OsString::from(name), OsString::from(value)));
}
return result;
}
fn appended(index: u64) -> Vec<(OsString, OsString)> {
return environment(&[
(COUNT_VARIABLE, (index + 1).to_string().as_str()),
(
format!("GIT_CONFIG_KEY_{index}").as_str(),
"core.lockfilePid",
),
(format!("GIT_CONFIG_VALUE_{index}").as_str(), "true"),
]);
}
#[test]
fn overlay_appends_after_existing_entries() {
assert_eq!(lockfile_pid_overlay(&[]), appended(0));
assert_eq!(
lockfile_pid_overlay(environment(&[(COUNT_VARIABLE, "")]).as_slice()),
appended(0)
);
assert_eq!(
lockfile_pid_overlay(
environment(&[
(COUNT_VARIABLE, " 2"),
("GIT_CONFIG_KEY_0", "user.name"),
("GIT_CONFIG_VALUE_0", "A"),
("GIT_CONFIG_KEY_1", "core.lockfilePid"),
("GIT_CONFIG_VALUE_1", "false"),
])
.as_slice()
),
appended(2)
);
}
#[test]
fn overlay_is_idempotent_when_the_last_entry_is_true() {
for (key, value) in [
("core.lockfilePid", "true"),
("CORE.LOCKFILEPID", "TRUE"),
("core.lockfilepid", "Yes"),
("Core.LockfilePid", "on"),
("core.lockfilePid", "1"),
] {
let pairs: Vec<(OsString, OsString)> = environment(&[
(COUNT_VARIABLE, "2"),
("GIT_CONFIG_KEY_0", "core.lockfilePid"),
("GIT_CONFIG_VALUE_0", "false"),
("GIT_CONFIG_KEY_1", key),
("GIT_CONFIG_VALUE_1", value),
]);
assert_eq!(
lockfile_pid_overlay(pairs.as_slice()),
Vec::<(OsString, OsString)>::new(),
"{key}={value}"
);
}
let nested: Vec<(OsString, OsString)> = appended(0);
assert_eq!(
lockfile_pid_overlay(nested.as_slice()),
Vec::<(OsString, OsString)>::new()
);
}
#[test]
fn overlay_appends_when_the_effective_entry_is_not_true() {
for (first, last) in [
("true", "false"),
("true", "no"),
("true", "0"),
("true", ""),
("true", "truthy"),
("true", "2"),
] {
let pairs: Vec<(OsString, OsString)> = environment(&[
(COUNT_VARIABLE, "2"),
("GIT_CONFIG_KEY_0", "core.lockfilePid"),
("GIT_CONFIG_VALUE_0", first),
("GIT_CONFIG_KEY_1", "core.lockfilePid"),
("GIT_CONFIG_VALUE_1", last),
]);
assert_eq!(
lockfile_pid_overlay(pairs.as_slice()),
appended(2),
"{last:?}"
);
}
let other_key: Vec<(OsString, OsString)> = environment(&[
(COUNT_VARIABLE, "1"),
("GIT_CONFIG_KEY_0", "core.lockfilePidX"),
("GIT_CONFIG_VALUE_0", "true"),
]);
assert_eq!(lockfile_pid_overlay(other_key.as_slice()), appended(1));
let beyond: Vec<(OsString, OsString)> = environment(&[
(COUNT_VARIABLE, "0"),
("GIT_CONFIG_KEY_0", "core.lockfilePid"),
("GIT_CONFIG_VALUE_0", "true"),
]);
assert_eq!(lockfile_pid_overlay(beyond.as_slice()), appended(0));
}
#[test]
fn overlay_leaves_malformed_numbered_configuration_untouched() {
for pairs in [
environment(&[(COUNT_VARIABLE, "x")]),
environment(&[(COUNT_VARIABLE, "-1")]),
environment(&[(COUNT_VARIABLE, "2147483648")]),
environment(&[(COUNT_VARIABLE, "1")]),
environment(&[(COUNT_VARIABLE, "1"), ("GIT_CONFIG_KEY_0", "a.b")]),
environment(&[(COUNT_VARIABLE, "1"), ("GIT_CONFIG_VALUE_0", "v")]),
environment(&[
(COUNT_VARIABLE, "2147483647"),
("GIT_CONFIG_KEY_0", "core.lockfilePid"),
("GIT_CONFIG_VALUE_0", "false"),
]),
] {
assert_eq!(
lockfile_pid_overlay(pairs.as_slice()),
Vec::<(OsString, OsString)>::new(),
"{pairs:?}"
);
}
}
#[test]
fn child_overlay_adds_the_forward_target_marker() {
let overlay: Vec<(OsString, OsString)> =
child_environment_overlay(&[], Path::new("/usr/bin/git"));
let mut expected: Vec<(OsString, OsString)> = appended(0);
expected.push((
OsString::from(FORWARD_TARGET_VARIABLE),
OsString::from("/usr/bin/git"),
));
assert_eq!(overlay, expected);
let already_true: Vec<(OsString, OsString)> = appended(0);
assert_eq!(
child_environment_overlay(already_true.as_slice(), Path::new("relative/git")),
vec![(
OsString::from(FORWARD_TARGET_VARIABLE),
OsString::from("relative/git")
)]
);
}
#[cfg(unix)]
#[test]
fn forward_target_marker_preserves_non_utf8_paths() {
use std::os::unix::ffi::OsStringExt;
let raw: OsString = OsString::from_vec(b"/opt/g\xff/git".to_vec());
let overlay: Vec<(OsString, OsString)> =
child_environment_overlay(appended(0).as_slice(), Path::new(&raw));
assert_eq!(
overlay,
vec![(OsString::from(FORWARD_TARGET_VARIABLE), raw)]
);
}
#[test]
fn native_git_reads_the_overlay_and_explicit_configuration_wins() {
let inherited: Vec<(OsString, OsString)> = environment(&[
(COUNT_VARIABLE, "1"),
("GIT_CONFIG_KEY_0", "core.lockfilePid"),
("GIT_CONFIG_VALUE_0", "false"),
]);
let overlay: Vec<(OsString, OsString)> = lockfile_pid_overlay(inherited.as_slice());
for (extra, expected) in [
(Vec::<&str>::new(), "true\n"),
(vec!["-c", "core.lockfilePid=false"], "false\n"),
] {
let mut command: Command = Command::new("/usr/bin/git");
command
.env_clear()
.env("GIT_CONFIG_NOSYSTEM", "1")
.env("GIT_CONFIG_GLOBAL", "/nonexistent-global-config")
.current_dir("/");
for (name, value) in &inherited {
command.env(name, value);
}
for (name, value) in &overlay {
command.env(name, value);
}
let output = command
.args(extra)
.args(["config", "--get", "core.lockfilepid"])
.output()
.expect("native Git overlay probe");
assert!(
output.status.success(),
"{}",
String::from_utf8_lossy(&output.stderr)
);
assert_eq!(String::from_utf8_lossy(&output.stdout), expected);
}
}
#[test]
fn an_overlaid_environment_keeps_the_callers_variables_and_appends_the_overlay() {
let caller = environment(&[("HOME", "/home/user"), ("PATH", "/usr/bin"), ("SSH_AUTH_SOCK", "/run/agent")]);
let overlay = environment(&[("CLI_GIT_NATIVE_FORWARD_TARGET", "/usr/bin/git")]);
assert_eq!(
environment_with_overlay(caller.as_slice(), overlay.as_slice()),
environment(&[
("HOME", "/home/user"),
("PATH", "/usr/bin"),
("SSH_AUTH_SOCK", "/run/agent"),
("CLI_GIT_NATIVE_FORWARD_TARGET", "/usr/bin/git"),
])
);
}
#[test]
fn an_overlaid_environment_replaces_a_name_the_overlay_sets() {
let caller = environment(&[("GIT_CONFIG_COUNT", "1"), ("HOME", "/home/user"), ("GIT_CONFIG_COUNT", "7")]);
let overlay = environment(&[("GIT_CONFIG_COUNT", "2"), ("GIT_CONFIG_KEY_1", "core.lockfilePid")]);
assert_eq!(
environment_with_overlay(caller.as_slice(), overlay.as_slice()),
environment(&[
("HOME", "/home/user"),
("GIT_CONFIG_COUNT", "2"),
("GIT_CONFIG_KEY_1", "core.lockfilePid"),
])
);
}
#[test]
fn an_overlaid_environment_with_an_empty_side_is_the_other_side() {
let caller = environment(&[("HOME", "/home/user")]);
assert_eq!(environment_with_overlay(caller.as_slice(), &[]), caller);
assert_eq!(environment_with_overlay(&[], caller.as_slice()), caller);
}