#[cfg(feature = "gedzip")]
use gedcomkit::gedzip::Archive;
use gedcomkit::{Document, decode_gedcom};
use std::path::PathBuf;
fn corpus_path(variable: &str) -> Option<PathBuf> {
let path = PathBuf::from(std::env::var_os(variable)?);
path.is_file().then_some(path)
}
#[test]
fn the_requirement_gedcom_reads_and_writes_back_byte_for_byte() {
let Some(path) = corpus_path("GEDCOMKIT_CORPUS_GED") else {
eprintln!("skipped: set GEDCOMKIT_CORPUS_GED to the requirement corpus document");
return;
};
let bytes = std::fs::read(&path).expect("read the corpus document");
let (text, encoding) = decode_gedcom(&bytes).expect("decode the corpus document");
let document = Document::parse(&text).expect("parse the corpus document");
let individuals = document.records_of("INDI").count();
let families = document.records_of("FAM").count();
let sources = document.records_of("SOUR").count();
let media = document.records_of("OBJE").count();
eprintln!(
"{}: {} lines, {individuals} individuals, {families} families, {sources} sources, \
{media} media objects, {}",
path.display(),
document.line_count(),
encoding.summary()
);
assert_eq!(
document.to_text(),
text,
"the document did not write back byte for byte"
);
assert!(individuals > 0, "expected at least one individual");
assert_eq!(document.version().as_deref(), Some("5.5.1"));
let dangling = document.unresolved_pointers();
assert!(
dangling.is_empty(),
"{} pointers do not resolve, first is {:?}",
dangling.len(),
dangling.first()
);
}
#[cfg(feature = "gedzip")]
#[test]
fn the_requirement_archive_opens_and_its_document_reads_back_byte_for_byte() {
let Some(path) = corpus_path("GEDCOMKIT_CORPUS_GEDZ") else {
eprintln!("skipped: set GEDCOMKIT_CORPUS_GEDZ to the requirement corpus archive");
return;
};
let bytes = std::fs::read(&path).expect("read the corpus archive");
let archive = Archive::open(&bytes).expect("open the corpus archive");
let name = archive
.document_name()
.expect("find the document")
.to_owned();
eprintln!(
"{}: {} entries, document is {name}",
path.display(),
archive.entries().len()
);
let document_bytes = archive.read(&name).expect("read the document");
let (text, _) = decode_gedcom(&document_bytes).expect("decode the document");
let document = Document::parse(&text).expect("parse the document");
assert_eq!(document.to_text(), text);
let mut media_bytes = 0usize;
for entry in archive.entries() {
if entry.is_directory || entry.name == name {
continue;
}
let content = archive
.read_entry(entry)
.unwrap_or_else(|failure| panic!("{}: {failure}", entry.name));
media_bytes += content.len();
}
eprintln!("{media_bytes} bytes of media read and checksummed");
assert!(media_bytes > 0, "the archive carried no media");
}
#[test]
fn the_checks_say_something_useful_about_the_requirement_corpus() {
let Some(path) = corpus_path("GEDCOMKIT_CORPUS_GED") else {
eprintln!("skipped: set GEDCOMKIT_CORPUS_GED to the requirement corpus document");
return;
};
let bytes = std::fs::read(&path).expect("read the corpus document");
let (text, _) = decode_gedcom(&bytes).expect("decode the corpus document");
let document = Document::parse(&text).expect("parse the corpus document");
let people = document.records_of("INDI").count();
let started = std::time::Instant::now();
let found = gedcomkit::validate::inspect(&document);
let took = started.elapsed();
let mut counted: std::collections::BTreeMap<&str, usize> = std::collections::BTreeMap::new();
for finding in &found {
*counted.entry(finding.check).or_default() += 1;
}
eprintln!(
"checks: {} findings over {people} people in {took:?}",
found.len()
);
for (check, count) in &counted {
eprintln!(" {check:>28}: {count}");
}
let mut shown: std::collections::BTreeSet<&str> = std::collections::BTreeSet::new();
for finding in &found {
if finding.severity != gedcomkit::validate::Severity::Missing && shown.insert(finding.check)
{
eprintln!(" e.g. {} — {}", finding.record, finding.message);
}
}
assert!(
took.as_millis() < 2_000,
"inspecting the file took {took:?}, which is on the path between a click and a report"
);
let contradictions = found
.iter()
.filter(|finding| finding.severity == gedcomkit::validate::Severity::Contradiction)
.count();
assert!(
contradictions * 5 < people,
"{contradictions} contradictions among {people} people is a broken check, not a broken file"
);
}