mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-28 12:27:11 -06:00
#3692: review — pdf and render timing tests get a margin a busy CI machine can't break
Wall-clock bounds of 1-2 s failed on loaded runners. Each bound is now 10 s, at least 10x the fixed code's time on a loaded machine and well under the old code's on the same input: - cluster ends: a million marks (was 200,000); 30 ms now. The rescan (the test's brute) took 2.9 s at 50,000 and is quadratic: about 20 minutes at a million. - clipped-cell lookups: 100,000 cells and lines (was 20,000); 0.25 s now, a pass over the cells per line would be 10^10 steps. - metafile text with a huge font: ~20 ms now; with the size check off it ran past 4 minutes. - hostile dash patterns: milliseconds now; drawn as dashes they ran past 4 minutes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CX1hMKX28764Lrx5moxs1o
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1225f56187
commit
77402c4e7e
+17
-12
@@ -2438,14 +2438,17 @@ fn cluster_ends_match_the_next_larger_cluster_in_linear_time() {
|
||||
for cl in [vec![], vec![0], vec![0, 0, 3, 3, 5], vec![5, 3, 3, 0], vec![2, 0, 4, 1, 1, 6, 3], vec![0, 7, 2, 2, 9, 1]] {
|
||||
assert_eq!(cluster_ends(&cl, 10), brute(&cl, 10), "{cl:?}");
|
||||
}
|
||||
// One base letter and 200 000 combining marks in its cluster, then one more letter.
|
||||
let mut cl = vec![0; 200_001];
|
||||
cl.push(400_002);
|
||||
// One base letter and a million combining marks in its cluster, then one more letter.
|
||||
// Milliseconds; the rescan (`brute`) took 2.9 s at 50,000 and grows with the square, so
|
||||
// about 20 minutes here.
|
||||
let n = 1_000_000;
|
||||
let mut cl = vec![0; n + 1];
|
||||
cl.push(2 * n + 2);
|
||||
let start = std::time::Instant::now();
|
||||
let ends = cluster_ends(&cl, 400_004);
|
||||
assert!(start.elapsed() < std::time::Duration::from_secs(1), "{:?}", start.elapsed());
|
||||
assert!(ends[..200_001].iter().all(|e| *e == 400_002));
|
||||
assert_eq!(ends.last(), Some(&400_004));
|
||||
let ends = cluster_ends(&cl, 2 * n + 4);
|
||||
assert!(start.elapsed() < std::time::Duration::from_secs(10), "{:?}", start.elapsed());
|
||||
assert!(ends[..=n].iter().all(|e| *e == 2 * n + 2));
|
||||
assert_eq!(ends.last(), Some(&(2 * n + 4)));
|
||||
}
|
||||
|
||||
/// A line in a cell cut off at its edge is drawn with that cell's clip, found by path in an index
|
||||
@@ -2477,11 +2480,13 @@ fn a_line_finds_its_clipped_cell_by_path() {
|
||||
assert_eq!(found(StoryRef::Part(7), &[0, 0, 0, 2]), Some(vec![0]));
|
||||
assert_eq!(found(StoryRef::Body, &[0, 0, 0, 2]), None, "another story");
|
||||
|
||||
// 20 000 clipped cells and as many lines: a lookup each, not a pass over the cells each.
|
||||
let big = Page { items: (0..20_000).map(|r| cell(StoryRef::Body, vec![0], r, 0, true)).collect(), ..Default::default() };
|
||||
// 100 000 clipped cells and as many lines: a lookup each, not a pass over the cells each
|
||||
// (10^10 steps, minutes; the lookups take well under a second on a loaded machine).
|
||||
let n = 100_000;
|
||||
let big = Page { items: (0..n).map(|r| cell(StoryRef::Body, vec![0], r, 0, true)).collect(), ..Default::default() };
|
||||
let start = std::time::Instant::now();
|
||||
let clips = clipped_cells(&big);
|
||||
let hits = (0..20_000u32).filter(|r| clip_cell(&clips, StoryRef::Body, &[0, *r, 0, 0]).is_some()).count();
|
||||
assert_eq!(hits, 20_000);
|
||||
assert!(start.elapsed() < std::time::Duration::from_secs(1), "{:?}", start.elapsed());
|
||||
let hits = (0..n as u32).filter(|r| clip_cell(&clips, StoryRef::Body, &[0, *r, 0, 0]).is_some()).count();
|
||||
assert_eq!(hits, n);
|
||||
assert!(start.elapsed() < std::time::Duration::from_secs(10), "{:?}", start.elapsed());
|
||||
}
|
||||
|
||||
@@ -2488,7 +2488,8 @@ mod tests {
|
||||
let l = layout(&d, &mut LayoutCache::new(), &LayoutOptions::default());
|
||||
let start = std::time::Instant::now();
|
||||
let img = render_page(&d, &l.pages[0], 1.0, &RenderOptions::default());
|
||||
assert!(start.elapsed() < std::time::Duration::from_secs(2), "{:?}", start.elapsed());
|
||||
// About 20 ms without the huge glyphs; drawing them ran past 4 minutes without finishing.
|
||||
assert!(start.elapsed() < std::time::Duration::from_secs(10), "{:?}", start.elapsed());
|
||||
assert_eq!(img.width, 612);
|
||||
// Leaving them out is logged (once a run, so it may have been by another test already).
|
||||
assert!(OVERSIZED_GLYPHS_NOTED.load(std::sync::atomic::Ordering::Relaxed), "the left-out glyphs are logged");
|
||||
@@ -2675,7 +2676,8 @@ mod tests {
|
||||
let img = render_items(&Document::new(), &[line(dash.clone())], 0.0, 0.0, 100.0, 40.0, 1.0, &RenderOptions::default());
|
||||
assert!(img.pixel(50, 20)[3] > 200, "{dash:?} draws solid");
|
||||
}
|
||||
assert!(start.elapsed() < std::time::Duration::from_secs(2), "{:?}", start.elapsed());
|
||||
// Milliseconds drawn solid; drawn as dashes they ran past 4 minutes without finishing.
|
||||
assert!(start.elapsed() < std::time::Duration::from_secs(10), "{:?}", start.elapsed());
|
||||
let dashed = render_items(&Document::new(), &[line(vec![10.0, 10.0])], 0.0, 0.0, 100.0, 40.0, 1.0, &RenderOptions::default());
|
||||
assert!(dashed.pixel(15, 20)[3] > 200 && dashed.pixel(25, 20)[3] == 0, "a normal pattern dashes");
|
||||
assert!(safe_dashes([3.0, 1.0], 1000.0).len() == 2);
|
||||
|
||||
Reference in New Issue
Block a user