use super::*;
use crate::test_support::{executable, fixture, git_context, remove};
use crate::transaction_git::GitContext;
use std::path::PathBuf;
use std::sync::OnceLock;
#[test]
fn release_numbers_are_read_from_plain_and_suffixed_output() {
assert_eq!(parse_git_version(b"git version 2.56.0\n"), Some((2, 56, 0)));
assert_eq!(parse_git_version(b"git version 2.56.0.windows.1\n"), Some((2, 56, 0)));
assert_eq!(parse_git_version(b"git version 2.56.0-rc1\n"), Some((2, 56, 0)));
assert_eq!(parse_git_version(b"git version 2.56.0a\n"), Some((2, 56, 0)));
assert_eq!(parse_git_version(b"git version 2.55.9\n"), Some((2, 55, 9)));
assert_eq!(parse_git_version(b"git version 10.0.12 extra\n"), Some((10, 0, 12)));
}
#[test]
fn unreadable_version_output_is_refused() {
assert_eq!(parse_git_version(b""), None);
assert_eq!(parse_git_version(b"version 2.56.0\n"), None);
assert_eq!(parse_git_version(b"git version 2.56\n"), None);
assert_eq!(parse_git_version(b"git version x.56.0\n"), None);
assert_eq!(parse_git_version(b"git version 2.56.x\n"), None);
assert_eq!(parse_git_version(b"git version \n"), None);
assert_eq!(parse_git_version(&[0xff, 0xfe, 0x00]), None);
}
#[test]
fn the_minimum_is_inclusive() {
assert_eq!(MINIMUM_GIT_VERSION, (2, 55, 0));
assert!((2, 55, 0) >= MINIMUM_GIT_VERSION);
assert!((2, 56, 0) >= MINIMUM_GIT_VERSION);
assert!((3, 0, 0) >= MINIMUM_GIT_VERSION);
assert!((2, 54, 9) < MINIMUM_GIT_VERSION);
assert!((2, 50, 0) < MINIMUM_GIT_VERSION);
}
#[test]
fn the_unsupported_message_names_the_git_and_the_remedy() {
let context: GitContext = git_context();
let message: String = unsupported_message(&context, "version 2.55.0");
assert!(message.contains("/usr/bin/git"));
assert!(message.contains("version 2.55.0"));
assert!(message.contains("2.55.0 or newer"));
assert!(message.contains("Upgrade Git"));
}
#[cfg(unix)]
fn fake_git(name: &str, line: &str) -> (PathBuf, GitContext) {
let root: PathBuf = fixture(name);
let path: PathBuf = root.join("git");
executable(
path.as_path(),
format!("#!/bin/sh\necho '{line}'\n").as_bytes(),
);
let context: GitContext = GitContext {
real_git: path.clone(),
overlay: crate::test_support::isolated_overlay(),
global_prefix: Vec::new(),
};
return (root, context);
}
#[test]
#[cfg(unix)]
fn a_supported_release_is_accepted() {
let (root, context): (PathBuf, GitContext) = fake_git("git-version-supported", "git version 2.55.0");
assert_eq!(ask_git_version(&context), Ok((2, 55, 0)));
remove(root.as_path());
}
#[test]
#[cfg(unix)]
fn an_older_release_is_refused() {
let (root, context): (PathBuf, GitContext) = fake_git("git-version-older", "git version 2.54.0");
let refusal: String = ask_git_version(&context).expect_err("2.54.0 must be refused");
assert!(refusal.contains("version 2.54.0"));
assert!(refusal.contains("2.55.0 or newer"));
remove(root.as_path());
}
#[test]
#[cfg(unix)]
fn an_unreadable_answer_is_refused() {
let (root, context): (PathBuf, GitContext) = fake_git("git-version-unreadable", "hello");
let refusal: String = ask_git_version(&context).expect_err("unreadable output must be refused");
assert!(refusal.contains("an unreadable version"));
assert!(refusal.contains("hello"));
remove(root.as_path());
}
#[test]
fn a_missing_git_is_refused() {
let root: PathBuf = fixture("git-version-missing");
let context: GitContext = GitContext {
real_git: root.join("no-such-git"),
overlay: Vec::new(),
global_prefix: Vec::new(),
};
let refusal: String = ask_git_version(&context).expect_err("a missing Git must be refused");
assert!(refusal.contains("nothing ("));
remove(root.as_path());
}
static ASKS: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
fn counting_supported(_context: &GitContext) -> Result<(u64, u64, u64), String> {
ASKS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
return Ok((2, 55, 0));
}
#[test]
fn the_answer_is_asked_once_and_then_reused() {
let cell: OnceLock<Result<(u64, u64, u64), String>> = OnceLock::new();
let context: GitContext = git_context();
let before: usize = ASKS.load(std::sync::atomic::Ordering::SeqCst);
assert_eq!(check_once(&cell, &context, counting_supported), Ok((2, 55, 0)));
assert_eq!(check_once(&cell, &context, counting_supported), Ok((2, 55, 0)));
assert_eq!(ASKS.load(std::sync::atomic::Ordering::SeqCst) - before, 1);
}
#[test]
fn a_refusal_is_reused_without_asking_again() {
fn refused(context: &GitContext) -> Result<(u64, u64, u64), String> {
return Err(unsupported_message(context, "version 2.55.0"));
}
let cell: OnceLock<Result<(u64, u64, u64), String>> = OnceLock::new();
let context: GitContext = git_context();
let first: Result<(u64, u64, u64), String> = check_once(&cell, &context, refused);
let second: Result<(u64, u64, u64), String> = check_once(&cell, &context, counting_supported);
assert_eq!(first, second);
assert!(first.expect_err("refused").contains("version 2.55.0"));
}
#[test]
fn the_process_check_answers_what_git_reports() {
let context: GitContext = git_context();
let direct: Result<GitRelease, String> = ask_git_version(&context);
let checked: Result<GitRelease, String> = check_git_version(&context);
assert_eq!(checked, direct);
assert!(checked.expect("the host Git is supported") >= MINIMUM_GIT_VERSION);
}