test: Add snapshot regression tests with PDF fixtures

Add 5 stripped public-domain PDF fixtures and golden markdown snapshots
for CI regression testing. Fix non-deterministic output caused by
HashMap iteration order in font stats, table heuristics, and rect
clustering by adding deterministic tie-breaking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Abimael Martell
2026-02-18 16:13:55 -08:00
co-authored by Claude Opus 4.6
parent ceb0cf5913
commit 28313e1f2d
14 changed files with 596 additions and 7 deletions
+8 -2
View File
@@ -21,9 +21,12 @@ pub(crate) fn calculate_font_stats_from_items(items: &[TextItem]) -> FontStats {
}
}
// Break ties by preferring the smaller font size for deterministic output
let most_common_size = size_counts
.iter()
.max_by_key(|(_, count)| *count)
.max_by(|(size_a, count_a), (size_b, count_b)| {
count_a.cmp(count_b).then_with(|| size_b.cmp(size_a))
})
.map(|(size, _)| *size as f32 / 10.0)
.unwrap_or(12.0);
@@ -45,9 +48,12 @@ pub(crate) fn calculate_font_stats(lines: &[TextLine]) -> FontStats {
}
}
// Break ties by preferring the smaller font size for deterministic output
let most_common_size = size_counts
.iter()
.max_by_key(|(_, count)| *count)
.max_by(|(size_a, count_a), (size_b, count_b)| {
count_a.cmp(count_b).then_with(|| size_b.cmp(size_a))
})
.map(|(size, _)| *size as f32 / 10.0)
.unwrap_or(12.0);
+4 -1
View File
@@ -654,9 +654,12 @@ fn has_consistent_columns(cells: &[Vec<String>]) -> bool {
*count_freq.entry(count).or_insert(0) += 1;
}
// Break ties by preferring higher column count for deterministic output
let most_common_count = count_freq
.iter()
.max_by_key(|(_, freq)| *freq)
.max_by(|(count_a, freq_a), (count_b, freq_b)| {
freq_a.cmp(freq_b).then_with(|| count_a.cmp(count_b))
})
.map(|(count, _)| *count)
.unwrap_or(0);
+7 -4
View File
@@ -88,10 +88,13 @@ pub(crate) fn cluster_rects(
groups.entry(uf.find(i)).or_default().push(i);
}
groups
.into_values()
.filter(|g| g.len() >= min_size)
.collect()
// Sort by root index for deterministic output order
let mut result: Vec<(usize, Vec<usize>)> = groups
.into_iter()
.filter(|(_, g)| g.len() >= min_size)
.collect();
result.sort_by_key(|(root, _)| *root);
result.into_iter().map(|(_, g)| g).collect()
}
/// Detect tables from explicit rectangle (`re`) operators in the PDF.