Another project
1#![allow(
2 dead_code,
3 reason = "dead_code checks each test binary alone and misses helpers used only by sibling binaries"
4)]
5
6use std::path::PathBuf;
7
8use bone_render::{
9 AdapterPolicy, OffscreenContext, PixelDiff, PixelDiffThreshold, RenderError, SnapshotFrame,
10 ViewportExtent, ViewportPx, decode_png, encode_png,
11};
12
13const ADAPTER_RETRIES: u32 = 3;
14const ADAPTER_RETRY_BACKOFF_MS: u64 = 100;
15const GOLDEN_DIFF_TOLERANCE: f64 = 16.0 / 255.0;
16
17#[must_use]
18pub fn extent_square(side: u32) -> ViewportExtent {
19 ViewportExtent::square(ViewportPx::new(side))
20}
21
22#[must_use]
23pub fn make_context(extent: ViewportExtent) -> OffscreenContext {
24 let mut last_err: Option<RenderError> = None;
25 for attempt in 0..ADAPTER_RETRIES {
26 match pollster::block_on(OffscreenContext::new(extent, AdapterPolicy::Platform)) {
27 Ok(ctx) => return ctx,
28 Err(e @ (RenderError::NoAdapter(_) | RenderError::Device(_))) => {
29 last_err = Some(e);
30 let backoff_ms = ADAPTER_RETRY_BACKOFF_MS * u64::from(attempt + 1);
31 std::thread::sleep(std::time::Duration::from_millis(backoff_ms));
32 }
33 Err(e) => panic!("offscreen context init failed: {e}"),
34 }
35 }
36 match last_err {
37 Some(RenderError::NoAdapter(e)) => panic!(
38 "no wgpu adapter available after {ADAPTER_RETRIES} attempts, configure lavapipe or an iGPU: {e}"
39 ),
40 Some(RenderError::Device(e)) => {
41 panic!("wgpu device request failed after {ADAPTER_RETRIES} attempts: {e}")
42 }
43 Some(other) => {
44 panic!("offscreen context init failed after {ADAPTER_RETRIES} attempts: {other}")
45 }
46 None => unreachable!("retry loop always populates last_err on failure"),
47 }
48}
49
50pub fn check_golden(frame: &SnapshotFrame, golden_rel: &str, update_env: &str) {
51 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(golden_rel);
52 if std::env::var(update_env).is_ok() {
53 let Ok(bytes) = encode_png(frame) else {
54 panic!("encode_png failed");
55 };
56 if let Some(parent) = path.parent() {
57 assert!(
58 std::fs::create_dir_all(parent).is_ok(),
59 "failed to create goldens dir"
60 );
61 }
62 assert!(
63 std::fs::write(&path, &bytes).is_ok(),
64 "failed to write golden {}",
65 path.display()
66 );
67 return;
68 }
69 let Ok(bytes) = std::fs::read(&path) else {
70 panic!(
71 "golden missing at {}: rerun with {update_env}=1 to generate",
72 path.display()
73 );
74 };
75 let Ok((golden_extent, golden_rgba)) = decode_png(&bytes) else {
76 panic!("failed to decode golden PNG");
77 };
78 assert_eq!(golden_extent, frame.extent(), "golden extent drift");
79 let threshold = PixelDiffThreshold::new(GOLDEN_DIFF_TOLERANCE);
80 let Ok(report) = PixelDiff::compare(frame, &golden_rgba, threshold) else {
81 panic!("PixelDiff rejected inputs");
82 };
83 assert!(
84 report.is_clean(),
85 "{golden_rel} drifted from golden: {} mismatches, worst {:?}, backend {}",
86 report.over_threshold(),
87 report.worst(),
88 frame.backend(),
89 );
90}