use gedcomkit::convert::{ConversionKind, to_version_5_5_1, to_version_7};
use gedcomkit::{Document, decode_gedcom};
use std::path::{Path, PathBuf};
fn fixture(relative: &str) -> Option<PathBuf> {
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("fixtures/external")
.join(relative);
path.is_file().then_some(path)
}
fn read(path: &Path) -> Document {
let bytes = std::fs::read(path).expect("read the fixture");
let (text, _) = decode_gedcom(&bytes).expect("decode the fixture");
Document::parse(&text).expect("parse the fixture")
}
#[test]
fn the_maximal_version_seven_sample_converts_down_and_back() {
let Some(path) = fixture("gedcom-io/gedcom70/maximal70.ged") else {
eprintln!("skipped: run scripts/fetch-fixtures.ps1 for the gedcom.io samples");
return;
};
let original = read(&path);
let records = original.records.len();
assert!(records > 10, "the maximal sample should be large");
let down = to_version_5_5_1(&original);
assert_eq!(down.to, "5.5.1");
assert_eq!(
down.document.records.len(),
records,
"converting a version cannot lose a record"
);
let text = down.document.to_text();
assert!(text.contains("2 VERS 5.5.1"), "the version is declared");
assert!(
text.contains("1 CHAR UTF-8"),
"5.5.1 requires a character set"
);
assert!(!text.contains("\n1 SCHMA"), "the declarations went");
let kept: Vec<&str> = down
.notes
.iter()
.filter(|note| note.kind == ConversionKind::Kept)
.map(|note| note.tag.as_str())
.collect();
assert!(
!kept.is_empty(),
"the maximal sample uses constructs 5.5.1 has no place for"
);
for tag in &kept {
assert!(
text.contains(&format!(" {tag}")),
"{tag} was reported as kept and is not in the document"
);
}
let up = to_version_7(&down.document);
assert_eq!(up.to, "7.0");
assert_eq!(up.document.records.len(), records);
let text = up.document.to_text();
assert!(text.contains("2 VERS 7.0"));
assert!(
!text.contains("\n1 CHAR "),
"version 7 has no character set"
);
assert!(
Document::parse(&text).is_ok(),
"what a conversion writes has to read back"
);
}
#[test]
fn the_five_five_one_torture_files_convert_up() {
let mut ran = 0;
for name in [
"torture/TGC551.ged",
"torture/TGC55C.ged",
"torture/TGC551LF.ged",
] {
let Some(path) = fixture(name) else {
continue;
};
ran += 1;
let original = read(&path);
let outcome = to_version_7(&original);
assert_eq!(
outcome.document.records.len(),
original.records.len(),
"{name} lost a record"
);
let text = outcome.document.to_text();
assert!(text.contains("2 VERS 7.0"), "{name}");
assert!(!text.contains("\n1 CHAR "), "{name} kept its character set");
Document::parse(&text).unwrap_or_else(|error| {
panic!("{name} did not read back after conversion: {error}");
});
let shared: Vec<String> = outcome
.document
.records_of("SNOTE")
.filter_map(|record| record.xref().map(str::to_owned))
.collect();
for record in &outcome.document.records {
for node in record.walk() {
if node.tag() == "NOTE"
&& let Some(pointer) = node.pointer()
{
assert!(
!shared.iter().any(|xref| xref.eq_ignore_ascii_case(pointer)),
"{name} left a NOTE pointing at a shared note record"
);
}
}
}
assert!(
outcome.document.unresolved_pointers().len() <= original.unresolved_pointers().len(),
"{name} gained an unresolved pointer"
);
}
if ran == 0 {
eprintln!("skipped: run scripts/fetch-fixtures.ps1 for the 5.5.1 torture files");
}
}