Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

test(render): offscreen hamburger helper

Lewis: May this revision serve well! <lu5a@proton.me>

author
Lewis
date (May 10, 2026, 12:10 PM +0300) commit 3f86d7f8 parent 2d3d75e4 change-id smyzvmvm
+135 -142
+5 -1
crates/bone-document/src/sketch/mod.rs
··· 1348 1348 )); 1349 1349 } 1350 1350 1351 - fn line_with_third_point(s: Sketch, a: SketchEntityId, b: SketchEntityId) -> (Sketch, SketchEntityId, SketchEntityId) { 1351 + fn line_with_third_point( 1352 + s: Sketch, 1353 + a: SketchEntityId, 1354 + b: SketchEntityId, 1355 + ) -> (Sketch, SketchEntityId, SketchEntityId) { 1352 1356 let Ok((s, EditOutcome::Entity(line))) = 1353 1357 s.apply(SketchEdit::AddEntity(SketchEntity::line(a, b, false))) 1354 1358 else {
+1 -4
crates/bone-document/tests/folder_roundtrip.rs
··· 128 128 SketchRelation::Tangent(line, circle), 129 129 SketchRelation::Equal(line, line2), 130 130 SketchRelation::Concentric(arc, circle), 131 - SketchRelation::Midpoint { 132 - point: p2, 133 - line, 134 - }, 131 + SketchRelation::Midpoint { point: p2, line }, 135 132 SketchRelation::Fix(p0), 136 133 ]; 137 134 let s = relations.into_iter().fold(s, |s, relation| {
+6 -18
crates/bone-render/tests/arcs.rs
··· 2 2 3 3 use bone_document::{EditOutcome, Sketch, SketchEdit, SketchEntity}; 4 4 use bone_render::{ 5 - Camera2, OffscreenContext, PixelDiff, PixelDiffThreshold, PixelsPerMm, RenderError, 6 - SketchRenderer, SketchScene, Style, ViewportExtent, ViewportPx, decode_png, encode_png, 5 + Camera2, PixelDiff, PixelDiffThreshold, PixelsPerMm, SketchRenderer, SketchScene, Style, 6 + decode_png, encode_png, 7 7 }; 8 8 use bone_types::{Length, Point2, Point3, SketchEntityId, SketchPlaneBasis, Tolerance, UnitVec3}; 9 9 use uom::si::length::millimeter; 10 10 11 + mod common; 12 + 13 + use common::{extent_square as extent, make_context}; 14 + 11 15 const GOLDEN: &str = "tests/goldens/arc_circle_zoom100_256.png"; 12 16 const UPDATE_ENV: &str = "BONE_UPDATE_ARC_GOLDEN"; 13 17 const DIFF_TOLERANCE: f64 = 16.0 / 255.0; 14 - 15 - fn extent(side: u32) -> ViewportExtent { 16 - ViewportExtent::square(ViewportPx::new(side)) 17 - } 18 - 19 - fn make_context(extent: ViewportExtent) -> OffscreenContext { 20 - match pollster::block_on(OffscreenContext::new(extent)) { 21 - Ok(ctx) => ctx, 22 - Err(RenderError::NoAdapter(e)) => { 23 - panic!( 24 - "no wgpu adapter available, configure lavapipe or an iGPU for this test host: {e}" 25 - ); 26 - } 27 - Err(e) => panic!("offscreen context init failed: {e}"), 28 - } 29 - } 30 18 31 19 fn golden_path() -> PathBuf { 32 20 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(GOLDEN)
+3 -18
crates/bone-render/tests/clear.rs
··· 1 - use bone_render::{ 2 - ClearColor, OffscreenContext, PixelDiff, PixelDiffThreshold, RenderError, Style, 3 - ViewportExtent, ViewportPx, 4 - }; 1 + use bone_render::{ClearColor, PixelDiff, PixelDiffThreshold, Style, ViewportExtent, ViewportPx}; 5 2 6 - fn extent(side: u32) -> ViewportExtent { 7 - ViewportExtent::square(ViewportPx::new(side)) 8 - } 3 + mod common; 9 4 10 - fn make_context(extent: ViewportExtent) -> OffscreenContext { 11 - match pollster::block_on(OffscreenContext::new(extent)) { 12 - Ok(ctx) => ctx, 13 - Err(RenderError::NoAdapter(e)) => { 14 - panic!( 15 - "no wgpu adapter available, configure lavapipe or an iGPU for this test host: {e}" 16 - ); 17 - } 18 - Err(e) => panic!("offscreen context init failed: {e}"), 19 - } 20 - } 5 + use common::{extent_square as extent, make_context}; 21 6 22 7 fn expected_rgba(extent: ViewportExtent, color: ClearColor) -> Vec<u8> { 23 8 let pixel = color.to_rgba8();
+37
crates/bone-render/tests/common/mod.rs
··· 1 + use bone_render::{OffscreenContext, RenderError, ViewportExtent, ViewportPx}; 2 + 3 + const ADAPTER_RETRIES: u32 = 3; 4 + const ADAPTER_RETRY_BACKOFF_MS: u64 = 100; 5 + 6 + #[must_use] 7 + pub fn extent_square(side: u32) -> ViewportExtent { 8 + ViewportExtent::square(ViewportPx::new(side)) 9 + } 10 + 11 + #[must_use] 12 + pub fn make_context(extent: ViewportExtent) -> OffscreenContext { 13 + let mut last_err: Option<RenderError> = None; 14 + for attempt in 0..ADAPTER_RETRIES { 15 + match pollster::block_on(OffscreenContext::new(extent)) { 16 + Ok(ctx) => return ctx, 17 + Err(e @ (RenderError::NoAdapter(_) | RenderError::Device(_))) => { 18 + last_err = Some(e); 19 + let backoff_ms = ADAPTER_RETRY_BACKOFF_MS * u64::from(attempt + 1); 20 + std::thread::sleep(std::time::Duration::from_millis(backoff_ms)); 21 + } 22 + Err(e) => panic!("offscreen context init failed: {e}"), 23 + } 24 + } 25 + match last_err { 26 + Some(RenderError::NoAdapter(e)) => panic!( 27 + "no wgpu adapter available after {ADAPTER_RETRIES} attempts, configure lavapipe or an iGPU: {e}" 28 + ), 29 + Some(RenderError::Device(e)) => { 30 + panic!("wgpu device request failed after {ADAPTER_RETRIES} attempts: {e}") 31 + } 32 + Some(other) => { 33 + panic!("offscreen context init failed after {ADAPTER_RETRIES} attempts: {other}") 34 + } 35 + None => unreachable!("retry loop always populates last_err on failure"), 36 + } 37 + }
+7 -16
crates/bone-render/tests/construction_styling.rs
··· 2 2 3 3 use bone_document::{EditOutcome, Sketch, SketchEdit, SketchEntity}; 4 4 use bone_render::{ 5 - Camera2, OffscreenContext, PixelDiff, PixelDiffThreshold, PixelsPerMm, RenderError, 6 - SketchRenderer, SketchScene, SnapshotFrame, Style, ViewportExtent, ViewportPx, decode_png, 7 - encode_png, 5 + Camera2, OffscreenContext, PixelDiff, PixelDiffThreshold, PixelsPerMm, SketchRenderer, 6 + SketchScene, SnapshotFrame, Style, ViewportExtent, decode_png, encode_png, 8 7 }; 9 8 use bone_types::{Length, Point2, Point3, SketchEntityId, SketchPlaneBasis, Tolerance, UnitVec3}; 10 9 use uom::si::length::millimeter; 10 + 11 + mod common; 11 12 12 13 const REAL_GOLDEN: &str = "tests/goldens/construction_real_256.png"; 13 14 const DASHED_GOLDEN: &str = "tests/goldens/construction_dashed_256.png"; ··· 16 17 const DISTINCT_MIN_PIXELS: u32 = 50; 17 18 18 19 fn extent() -> ViewportExtent { 19 - ViewportExtent::square(ViewportPx::new(256)) 20 - } 21 - 22 - fn make_context() -> OffscreenContext { 23 - match pollster::block_on(OffscreenContext::new(extent())) { 24 - Ok(ctx) => ctx, 25 - Err(RenderError::NoAdapter(e)) => panic!( 26 - "no wgpu adapter available, configure lavapipe or an iGPU for this test host: {e}" 27 - ), 28 - Err(e) => panic!("offscreen context init failed: {e}"), 29 - } 20 + common::extent_square(256) 30 21 } 31 22 32 23 fn plane() -> SketchPlaneBasis { ··· 170 161 171 162 #[test] 172 163 fn real_and_dashed_goldens_match() { 173 - let ctx = make_context(); 164 + let ctx = common::make_context(extent()); 174 165 let real = render_scene(&ctx, &build_scene(false)); 175 166 match_or_update_golden(&real, REAL_GOLDEN); 176 167 let dashed = render_scene(&ctx, &build_scene(true)); ··· 179 170 180 171 #[test] 181 172 fn construction_render_differs_from_real_render() { 182 - let ctx = make_context(); 173 + let ctx = common::make_context(extent()); 183 174 let real = render_scene(&ctx, &build_scene(false)); 184 175 let dashed = render_scene(&ctx, &build_scene(true)); 185 176 assert_eq!(real.extent(), dashed.extent());
+6 -16
crates/bone-render/tests/dimensions.rs
··· 4 4 DimensionKind, EditOutcome, Sketch, SketchDimension, SketchEdit, SketchEntity, 5 5 }; 6 6 use bone_render::{ 7 - Camera2, OffscreenContext, PixelDiff, PixelDiffThreshold, PixelsPerMm, RenderError, 8 - SketchRenderer, SketchScene, Style, ViewportExtent, ViewportPx, decode_png, encode_png, 7 + Camera2, PixelDiff, PixelDiffThreshold, PixelsPerMm, SketchRenderer, SketchScene, Style, 8 + decode_png, encode_png, 9 9 }; 10 10 use bone_types::{ 11 11 Angle, Length, Point2, Point3, SketchEntityId, SketchPlaneBasis, Tolerance, UnitVec3, ··· 13 13 use uom::si::angle::degree; 14 14 use uom::si::length::millimeter; 15 15 16 + mod common; 17 + 18 + use common::{extent_square as extent, make_context}; 19 + 16 20 const GOLDEN: &str = "tests/goldens/dimensions_256.png"; 17 21 const UPDATE_ENV: &str = "BONE_UPDATE_DIMENSIONS_GOLDEN"; 18 22 const DIFF_TOLERANCE: f64 = 20.0 / 255.0; 19 - 20 - fn extent(side: u32) -> ViewportExtent { 21 - ViewportExtent::square(ViewportPx::new(side)) 22 - } 23 - 24 - fn make_context(extent: ViewportExtent) -> OffscreenContext { 25 - match pollster::block_on(OffscreenContext::new(extent)) { 26 - Ok(ctx) => ctx, 27 - Err(RenderError::NoAdapter(e)) => panic!( 28 - "no wgpu adapter available, configure lavapipe or an iGPU for this test host: {e}" 29 - ), 30 - Err(e) => panic!("offscreen context init failed: {e}"), 31 - } 32 - } 33 23 34 24 fn plane() -> SketchPlaneBasis { 35 25 let Ok(basis) = SketchPlaneBasis::new(
+6 -18
crates/bone-render/tests/grid.rs
··· 1 1 use std::path::PathBuf; 2 2 3 3 use bone_render::{ 4 - Camera2, OffscreenContext, PixelDiff, PixelDiffThreshold, RenderError, SketchRenderer, 5 - SketchScene, Style, ViewportExtent, ViewportPx, decode_png, encode_png, 4 + Camera2, PixelDiff, PixelDiffThreshold, SketchRenderer, SketchScene, Style, decode_png, 5 + encode_png, 6 6 }; 7 7 8 + mod common; 9 + 10 + use common::{extent_square as extent, make_context}; 11 + 8 12 const GOLDEN: &str = "tests/goldens/grid_empty_256.png"; 9 13 const UPDATE_ENV: &str = "BONE_UPDATE_GRID_GOLDEN"; 10 14 const DIFF_TOLERANCE: f64 = 16.0 / 255.0; 11 - 12 - fn extent(side: u32) -> ViewportExtent { 13 - ViewportExtent::square(ViewportPx::new(side)) 14 - } 15 - 16 - fn make_context(extent: ViewportExtent) -> OffscreenContext { 17 - match pollster::block_on(OffscreenContext::new(extent)) { 18 - Ok(ctx) => ctx, 19 - Err(RenderError::NoAdapter(e)) => { 20 - panic!( 21 - "no wgpu adapter available, configure lavapipe or an iGPU for this test host: {e}" 22 - ); 23 - } 24 - Err(e) => panic!("offscreen context init failed: {e}"), 25 - } 26 - } 27 15 28 16 fn golden_path() -> PathBuf { 29 17 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(GOLDEN)
+9 -17
crates/bone-render/tests/picker.rs
··· 1 1 use bone_document::{EditOutcome, Sketch, SketchEdit, SketchEntity}; 2 2 use bone_render::{ 3 - Camera2, OffscreenContext, PickQuery, PickedItem, RenderError, SketchRenderer, SketchScene, 4 - Style, ViewportExtent, ViewportPx, 3 + Camera2, PickQuery, PickedItem, RenderError, SketchRenderer, SketchScene, Style, 4 + ViewportExtent, ViewportPx, 5 5 }; 6 6 use bone_types::{Length, Point2, Point3, SketchEntityId, SketchPlaneBasis, Tolerance, UnitVec3}; 7 7 use uom::si::length::millimeter; 8 8 9 + mod common; 10 + 9 11 const SIDE: u32 = 256; 10 12 const CENTER_PX: f32 = 128.0; 11 13 const PX_PER_MM: f32 = 10.0; 12 14 13 15 fn extent() -> ViewportExtent { 14 - ViewportExtent::square(ViewportPx::new(SIDE)) 15 - } 16 - 17 - fn make_context() -> OffscreenContext { 18 - match pollster::block_on(OffscreenContext::new(extent())) { 19 - Ok(ctx) => ctx, 20 - Err(RenderError::NoAdapter(e)) => panic!( 21 - "no wgpu adapter available, configure lavapipe or an iGPU for this test host: {e}" 22 - ), 23 - Err(e) => panic!("offscreen context init failed: {e}"), 24 - } 16 + common::extent_square(SIDE) 25 17 } 26 18 27 19 fn plane() -> SketchPlaneBasis { ··· 132 124 133 125 #[test] 134 126 fn each_entity_centroid_round_trips_to_pick_id() { 135 - let ctx = make_context(); 127 + let ctx = common::make_context(extent()); 136 128 let mut renderer = SketchRenderer::new(ctx.gpu(), ctx.color_format()); 137 129 let camera = Camera2::new(extent()); 138 130 let style = Style::default(); ··· 176 168 177 169 #[test] 178 170 fn empty_space_decodes_to_none() { 179 - let ctx = make_context(); 171 + let ctx = common::make_context(extent()); 180 172 let mut renderer = SketchRenderer::new(ctx.gpu(), ctx.color_format()); 181 173 let camera = Camera2::new(extent()); 182 174 let style = Style::default(); ··· 199 191 200 192 #[test] 201 193 fn repeated_render_picks_deterministic() { 202 - let ctx = make_context(); 194 + let ctx = common::make_context(extent()); 203 195 let mut renderer = SketchRenderer::new(ctx.gpu(), ctx.color_format()); 204 196 let camera = Camera2::new(extent()); 205 197 let style = Style::default(); ··· 240 232 241 233 #[test] 242 234 fn out_of_bounds_query_is_rejected() { 243 - let ctx = make_context(); 235 + let ctx = common::make_context(extent()); 244 236 let Ok(index) = SketchScene::empty().pick_index() else { 245 237 panic!("pick index"); 246 238 };
+44
crates/bone-render/tests/preview.rs
··· 1 + use bone_render::{ 2 + Camera2, PixelsPerMm, PreviewArc, SketchPreview, SketchRenderer, SketchScene, Style, 3 + ViewportExtent, 4 + }; 5 + use bone_types::{Angle, Length, Point2}; 6 + use uom::si::angle::radian; 7 + use uom::si::length::millimeter; 8 + 9 + mod common; 10 + 11 + fn extent() -> ViewportExtent { 12 + common::extent_square(256) 13 + } 14 + 15 + #[test] 16 + fn preview_arc_paints_pixels_outside_background() { 17 + let ctx = common::make_context(extent()); 18 + let mut renderer = SketchRenderer::new(ctx.gpu(), ctx.color_format()); 19 + let scene = SketchScene::empty(); 20 + let preview = SketchPreview { 21 + arcs: vec![PreviewArc { 22 + center: Point2::from_mm(0.0, 0.0), 23 + radius: Length::new::<millimeter>(1.0), 24 + start_angle: Angle::new::<radian>(0.0), 25 + sweep_angle: Angle::new::<radian>(std::f64::consts::FRAC_PI_2), 26 + }], 27 + ..SketchPreview::empty() 28 + }; 29 + let camera = Camera2::new(extent()).with_zoom(PixelsPerMm::new(64.0)); 30 + let style = Style::default(); 31 + let Ok(frame) = renderer.render_with_preview(&ctx, &scene, &preview, camera, &style) else { 32 + panic!("render_with_preview failed"); 33 + }; 34 + let bg = style.background().to_rgba8(); 35 + let pixels = frame.rgba(); 36 + let painted = pixels 37 + .chunks_exact(4) 38 + .filter(|p| p[0] != bg[0] || p[1] != bg[1] || p[2] != bg[2]) 39 + .count(); 40 + assert!( 41 + painted > 0, 42 + "preview arc should paint pixels distinct from the {bg:?} background", 43 + ); 44 + }
+6 -18
crates/bone-render/tests/rectangle.rs
··· 2 2 3 3 use bone_document::{EditOutcome, Sketch, SketchEdit, SketchEntity}; 4 4 use bone_render::{ 5 - Camera2, OffscreenContext, PixelDiff, PixelDiffThreshold, RenderError, SketchRenderer, 6 - SketchScene, Style, ViewportExtent, ViewportPx, decode_png, encode_png, 5 + Camera2, PixelDiff, PixelDiffThreshold, SketchRenderer, SketchScene, Style, decode_png, 6 + encode_png, 7 7 }; 8 8 use bone_types::{Point2, Point3, SketchEntityId, SketchPlaneBasis, Tolerance, UnitVec3}; 9 9 10 + mod common; 11 + 12 + use common::{extent_square as extent, make_context}; 13 + 10 14 const GOLDEN: &str = "tests/goldens/rectangle_256.png"; 11 15 const UPDATE_ENV: &str = "BONE_UPDATE_RECTANGLE_GOLDEN"; 12 16 const DIFF_TOLERANCE: f64 = 16.0 / 255.0; 13 - 14 - fn extent(side: u32) -> ViewportExtent { 15 - ViewportExtent::square(ViewportPx::new(side)) 16 - } 17 - 18 - fn make_context(extent: ViewportExtent) -> OffscreenContext { 19 - match pollster::block_on(OffscreenContext::new(extent)) { 20 - Ok(ctx) => ctx, 21 - Err(RenderError::NoAdapter(e)) => { 22 - panic!( 23 - "no wgpu adapter available, configure lavapipe or an iGPU for this test host: {e}" 24 - ); 25 - } 26 - Err(e) => panic!("offscreen context init failed: {e}"), 27 - } 28 - } 29 17 30 18 fn golden_path() -> PathBuf { 31 19 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(GOLDEN)
+5 -16
crates/bone-render/tests/relations.rs
··· 3 3 use bone_document::{EditOutcome, Sketch, SketchEdit, SketchEntity, SketchRelation}; 4 4 use bone_render::{ 5 5 Camera2, OffscreenContext, PixelDiff, PixelDiffThreshold, PixelsPerMm, RelationGlyphKind, 6 - RenderError, SketchRenderer, SketchScene, Style, ViewportExtent, ViewportPx, decode_png, 7 - encode_png, 6 + SketchRenderer, SketchScene, Style, decode_png, encode_png, 8 7 }; 9 8 use bone_types::{Length, Point2, Point3, SketchEntityId, SketchPlaneBasis, Tolerance, UnitVec3}; 10 9 use uom::si::length::millimeter; 11 10 11 + mod common; 12 + 13 + use common::{extent_square as extent, make_context}; 14 + 12 15 const GOLDEN: &str = "tests/goldens/relations_256.png"; 13 16 const UPDATE_ENV: &str = "BONE_UPDATE_RELATIONS_GOLDEN"; 14 17 const DIFF_TOLERANCE: f64 = 16.0 / 255.0; 15 - 16 - fn extent(side: u32) -> ViewportExtent { 17 - ViewportExtent::square(ViewportPx::new(side)) 18 - } 19 - 20 - fn make_context(extent: ViewportExtent) -> OffscreenContext { 21 - match pollster::block_on(OffscreenContext::new(extent)) { 22 - Ok(ctx) => ctx, 23 - Err(RenderError::NoAdapter(e)) => panic!( 24 - "no wgpu adapter available, configure lavapipe or an iGPU for this test host: {e}" 25 - ), 26 - Err(e) => panic!("offscreen context init failed: {e}"), 27 - } 28 - } 29 18 30 19 fn plane() -> SketchPlaneBasis { 31 20 let Ok(basis) = SketchPlaneBasis::new(