use std::fs;
use std::path::{Path, PathBuf};
use flate2::read::GzDecoder;
use gitversion_rs::{config, git, version};
use serde_json::Value;
const COMPARED_KEYS: &[&str] = &[
"FullSemVer",
"SemVer",
"MajorMinorPatch",
"Major",
"Minor",
"Patch",
"PreReleaseLabel",
"PreReleaseLabelWithDash",
"PreReleaseNumber",
"PreReleaseTag",
"PreReleaseTagWithDash",
"BranchName",
"EscapedBranchName",
"CommitDate",
"AssemblySemVer",
"AssemblySemFileVer",
"InformationalVersion",
"WeightedPreReleaseNumber",
"VersionSourceDistance",
"VersionSourceIncrement",
"VersionSourceSemVer",
"Sha",
"ShortSha",
];
fn extract_fixtures() -> PathBuf {
let archive = Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/fixtures.tar.gz");
assert!(
archive.exists(),
"fixture 압축이 없습니다: {} (먼저 ./tests/build_fixtures.sh 실행)",
archive.display()
);
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let dest = std::env::temp_dir().join(format!(
"gitversion-fixtures-{}-{}",
std::process::id(),
nanos
));
fs::create_dir_all(&dest).unwrap();
let file = fs::File::open(&archive).unwrap();
let mut tar = tar::Archive::new(GzDecoder::new(file));
tar.unpack(&dest).unwrap();
dest
}
fn normalize(v: Option<&Value>) -> String {
match v {
Some(Value::String(s)) => s.clone(),
Some(Value::Number(n)) => n.to_string(),
Some(Value::Bool(b)) => b.to_string(),
Some(Value::Null) | None => String::new(),
Some(other) => other.to_string(),
}
}
#[test]
fn fixtures_match_real_gitversion() {
let root = extract_fixtures();
let mut scenario_dirs: Vec<PathBuf> = Vec::new();
for entry in fs::read_dir(&root).unwrap() {
let p = entry.unwrap().path();
if p.is_dir() && p.join("expected.json").exists() {
scenario_dirs.push(p);
}
}
scenario_dirs.sort();
assert!(
!scenario_dirs.is_empty(),
"시나리오를 찾지 못했습니다: {}",
root.display()
);
let mut failures: Vec<String> = Vec::new();
let mut checked = 0usize;
for dir in &scenario_dirs {
let name = dir.file_name().unwrap().to_string_lossy().to_string();
let expected_text = fs::read_to_string(dir.join("expected.json")).unwrap();
let expected: Value = serde_json::from_str(&expected_text).unwrap();
let repo = match git::GitRepo::discover(dir) {
Ok(r) => r,
Err(e) => {
failures.push(format!("[{name}] failed to open repo: {e}"));
continue;
}
};
let workdir = repo
.workdir()
.map(|p| p.to_path_buf())
.unwrap_or_else(|| dir.clone());
let configuration = config::loader::load(None, &workdir, Some(&workdir)).unwrap();
let vars = match version::calculation::calculate(&repo, &configuration, None) {
Ok(v) => v,
Err(e) => {
failures.push(format!("[{name}] calculation failed: {e}"));
continue;
}
};
let actual = vars.to_map();
for key in COMPARED_KEYS {
let exp = normalize(expected.get(*key));
let got = actual.get(*key).cloned().unwrap_or_default();
if exp != got {
failures.push(format!(
"[{name}] {key}: expected(real)={exp:?} actual(mine)={got:?}"
));
}
}
checked += 1;
}
let _ = fs::remove_dir_all(&root);
if !failures.is_empty() {
panic!(
"{}개 시나리오 중 불일치 {}건:\n{}",
checked,
failures.len(),
failures.join("\n")
);
}
println!("{checked}개 시나리오 모두 실제 GitVersion 과 일치");
}