Another project
0

Configure Feed

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

test(document): final touches for now

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

author
Lewis
date (Jun 5, 2026, 4:01 PM +0300) commit d460f186 parent 071cac58 change-id uyunumso
+230 -28
+39 -2
crates/bone-document/tests/folder_property.rs
··· 2 2 DimensionKind, Document, DocumentFolder, Sketch, SketchDimension, SketchEdit, SketchEntity, 3 3 SketchEntityKind, SketchParameter, SketchRelation, load, save, 4 4 }; 5 + use bone_kernel::{ 6 + ExtrudeDirection, ExtrudeEndCondition, ExtrudeFeature, ExtrudeSense, MergeResult, 7 + }; 5 8 use bone_types::{ 6 - Angle, DocumentId, Length, Parameter, Point2, Point3, SketchId, SketchPlaneBasis, Tolerance, 7 - UnitVec3, degree, millimeter, 9 + Angle, DocumentId, ExtrudeId, Length, Parameter, Point2, Point3, PositiveLength, SketchId, 10 + SketchPlaneBasis, Tolerance, UnitVec3, degree, millimeter, 8 11 }; 9 12 use proptest::prelude::*; 10 13 use slotmap::KeyData; ··· 36 39 37 40 fn document_id(idx: u32) -> DocumentId { 38 41 DocumentId::from(KeyData::from_ffi((1u64 << 32) | u64::from(idx))) 42 + } 43 + 44 + fn extrude_id(idx: u32) -> ExtrudeId { 45 + ExtrudeId::from(KeyData::from_ffi((1u64 << 32) | u64::from(idx))) 39 46 } 40 47 41 48 #[derive(Copy, Clone, Debug)] ··· 414 421 return Err(TestCaseError::Fail("sketch missing".into())); 415 422 }; 416 423 prop_assert_eq!(round, &sketch); 424 + } 425 + 426 + #[test] 427 + fn sketch_plus_extrude_roundtrips_whole_document( 428 + steps in prop::collection::vec(arb_step(), 0..30), 429 + depth_mm in 1u16..500, 430 + ) { 431 + let sketch = build(steps); 432 + let dir = tempdir().map_err(|e| TestCaseError::Fail(format!("tempdir: {e}").into()))?; 433 + let folder = DocumentFolder::new(dir.path().join("ext.bone")); 434 + let mut doc = Document::new(document_id(1), "ext".to_owned()); 435 + doc.insert_sketch(sketch_id(1), "S".to_owned(), sketch); 436 + let depth = PositiveLength::new(mm(f64::from(depth_mm))) 437 + .map_err(|e| TestCaseError::Fail(format!("depth: {e}").into()))?; 438 + doc.insert_extrude( 439 + extrude_id(1), 440 + ExtrudeFeature { 441 + sketch: sketch_id(1), 442 + direction: ExtrudeDirection::Normal { 443 + sense: ExtrudeSense::Forward, 444 + }, 445 + end_condition: ExtrudeEndCondition::Blind { depth }, 446 + draft: None, 447 + thin_wall: None, 448 + merge_result: MergeResult::Merge, 449 + }, 450 + ); 451 + save(&doc, &folder).map_err(|e| TestCaseError::Fail(format!("save: {e}").into()))?; 452 + let loaded = load(&folder).map_err(|e| TestCaseError::Fail(format!("load: {e}").into()))?; 453 + prop_assert_eq!(loaded, doc); 417 454 } 418 455 419 456 #[test]
+134 -21
crates/bone-document/tests/folder_roundtrip.rs
··· 1 1 use bone_document::{ 2 2 BlobHash, BlobKind, Document, DocumentFolder, DocumentHeader, EditOutcome, Sketch, 3 3 SketchDimension, SketchEdit, SketchEntity, SketchRegistry, SketchRegistryEntry, SketchRelation, 4 - from_str, load, save, to_string, 4 + evaluate_extrude, evaluate_sketch, from_str, load, save, to_string, 5 5 }; 6 6 use bone_kernel::{ 7 - ExtrudeDirection, ExtrudeEndCondition, ExtrudeFeature, ExtrudeSense, MergeResult, 7 + BrepFace, ExtrudeDirection, ExtrudeEndCondition, ExtrudeFeature, ExtrudeSense, MergeResult, 8 8 }; 9 9 use bone_types::{ 10 10 Angle, DocumentId, ExtrudeId, Length, Point2, Point3, PositiveLength, SketchEntityId, SketchId, ··· 570 570 doc.insert_extrude(extrude_id(1), blind_extrude(sketch_id(1))); 571 571 assert_save(&doc, &folder); 572 572 573 + assert!( 574 + folder.extrude_path(extrude_id(1)).exists(), 575 + "extrude lands in its own file under extrudes/" 576 + ); 577 + let document_ron = read_file(&folder.document_file()); 578 + assert!( 579 + !document_ron.contains("ExtrudeFeature"), 580 + "extrude feature must not inline into document.ron:\n{document_ron}" 581 + ); 582 + 573 583 let loaded = assert_load(&folder); 584 + assert_eq!(loaded, doc, "full document survives the round-trip"); 574 585 let Some(feature) = loaded.feature_tree().feature_of_extrude(extrude_id(1)) else { 575 586 panic!("extrude node survives the round-trip"); 576 587 }; ··· 601 612 } 602 613 603 614 #[test] 604 - fn load_refuses_tree_extrude_without_entry() { 615 + fn load_refuses_tree_extrude_without_file() { 605 616 let dir = ok_dir(); 606 - let folder = DocumentFolder::new(dir.path().join("dangling_extrude.bone")); 607 - let mut doc = Document::new(document_id(1), "dangling".to_owned()); 617 + let folder = DocumentFolder::new(dir.path().join("missing_extrude.bone")); 618 + let mut doc = Document::new(document_id(1), "missing".to_owned()); 608 619 doc.insert_sketch(sketch_id(1), "S".to_owned(), rectangle()); 609 620 doc.insert_extrude(extrude_id(1), blind_extrude(sketch_id(1))); 610 621 assert_save(&doc, &folder); 611 622 612 - patch_header(&folder.document_file(), |h| { 613 - h.extrudes.remove(&extrude_id(1)); 614 - }); 623 + let Ok(()) = std::fs::remove_file(folder.extrude_path(extrude_id(1))) else { 624 + panic!("remove extrude file"); 625 + }; 615 626 616 627 let result = load(&folder).map_err(bone_document::FolderError::into_kind); 617 - let Err(bone_document::FolderErrorKind::DanglingTreeExtrude { id }) = result else { 618 - panic!("expected DanglingTreeExtrude, got {result:?}"); 628 + let Err(bone_document::FolderErrorKind::MissingExtrudeFile { id }) = result else { 629 + panic!("expected MissingExtrudeFile, got {result:?}"); 619 630 }; 620 631 assert_eq!(id, extrude_id(1)); 621 632 } 622 633 623 634 #[test] 624 - fn load_refuses_orphan_extrude() { 635 + fn removing_extrude_drops_stale_file() { 625 636 let dir = ok_dir(); 626 - let folder = DocumentFolder::new(dir.path().join("orphan_extrude.bone")); 627 - let mut doc = Document::new(document_id(1), "orphan".to_owned()); 637 + let folder = DocumentFolder::new(dir.path().join("drop_extrude.bone")); 638 + let mut doc = Document::new(document_id(1), "drop".to_owned()); 628 639 doc.insert_sketch(sketch_id(1), "S".to_owned(), rectangle()); 629 640 doc.insert_extrude(extrude_id(1), blind_extrude(sketch_id(1))); 630 641 assert_save(&doc, &folder); 642 + assert!(folder.extrude_path(extrude_id(1)).exists()); 631 643 632 - patch_header(&folder.document_file(), |h| { 633 - h.feature_tree.remove_extrude(extrude_id(1)); 634 - }); 644 + doc.remove_extrude(extrude_id(1)); 645 + assert_save(&doc, &folder); 646 + assert!(!folder.extrude_path(extrude_id(1)).exists()); 635 647 636 - let result = load(&folder).map_err(bone_document::FolderError::into_kind); 637 - let Err(bone_document::FolderErrorKind::OrphanExtrude { id }) = result else { 638 - panic!("expected OrphanExtrude, got {result:?}"); 639 - }; 640 - assert_eq!(id, extrude_id(1)); 648 + let loaded = assert_load(&folder); 649 + assert!( 650 + loaded 651 + .feature_tree() 652 + .feature_of_extrude(extrude_id(1)) 653 + .is_none() 654 + ); 641 655 } 642 656 643 657 #[test] ··· 674 688 panic!("expected OrphanRegistered, got {result:?}"); 675 689 }; 676 690 assert_eq!(id, sketch_id(1)); 691 + } 692 + 693 + fn closed_rectangle(width_mm: f64) -> Sketch { 694 + let s = Sketch::new(plane()); 695 + let (s, p0) = add_entity_returning_id(s, SketchEntity::point(Point2::from_mm(0.0, 0.0))); 696 + let (s, p1) = add_entity_returning_id(s, SketchEntity::point(Point2::from_mm(width_mm, 0.0))); 697 + let (s, p2) = add_entity_returning_id(s, SketchEntity::point(Point2::from_mm(width_mm, 5.0))); 698 + let (s, p3) = add_entity_returning_id(s, SketchEntity::point(Point2::from_mm(0.0, 5.0))); 699 + [(p0, p1), (p1, p2), (p2, p3), (p3, p0)] 700 + .into_iter() 701 + .fold(s, |s, (a, b)| { 702 + let Ok((next, _)) = s.apply(SketchEdit::AddEntity(SketchEntity::line(a, b, false))) 703 + else { 704 + panic!("rectangle edge"); 705 + }; 706 + next 707 + }) 708 + } 709 + 710 + fn evaluated_solid(doc: &Document, sketch: SketchId, extrude: ExtrudeId) -> bone_kernel::BrepSolid { 711 + let Some(sketch_value) = doc.sketch(sketch) else { 712 + panic!("sketch present"); 713 + }; 714 + let Some(feature_id) = doc.feature_tree().feature_of_extrude(extrude) else { 715 + panic!("extrude node present"); 716 + }; 717 + let Some(&feature) = doc.extrude_of_feature(feature_id) else { 718 + panic!("extrude feature present"); 719 + }; 720 + let evaluated = evaluate_sketch(sketch_value); 721 + let extruded = evaluate_extrude(feature_id, &evaluated, &feature); 722 + let Some(solid) = extruded.solid() else { 723 + panic!("rectangle extrudes to a solid"); 724 + }; 725 + solid.clone() 726 + } 727 + 728 + fn face_labels(solid: &bone_kernel::BrepSolid) -> Vec<bone_types::FaceLabel> { 729 + solid.iter_faces().map(BrepFace::label).collect() 730 + } 731 + 732 + #[test] 733 + fn width_change_yields_text_diff_and_new_blob() { 734 + let dir = ok_dir(); 735 + let folder = DocumentFolder::new(dir.path().join("edit.bone")); 736 + let sid = sketch_id(1); 737 + let eid = extrude_id(1); 738 + let mut doc = Document::new(document_id(1), "edit".to_owned()); 739 + doc.insert_sketch(sid, "S".to_owned(), closed_rectangle(10.0)); 740 + doc.insert_extrude(eid, blind_extrude(sid)); 741 + assert_save(&doc, &folder); 742 + 743 + let document_before = read_file(&folder.document_file()); 744 + let sketch_before = read_file(&folder.sketch_path(sid)); 745 + let solid_before = evaluated_solid(&doc, sid, eid); 746 + let Ok(hash_before) = bone_document::write_solid(&folder, &solid_before) else { 747 + panic!("cache first solid"); 748 + }; 749 + 750 + doc.replace_sketch(sid, closed_rectangle(14.0)); 751 + assert_save(&doc, &folder); 752 + 753 + let document_after = read_file(&folder.document_file()); 754 + let sketch_after = read_file(&folder.sketch_path(sid)); 755 + let solid_after = evaluated_solid(&doc, sid, eid); 756 + let Ok(hash_after) = bone_document::write_solid(&folder, &solid_after) else { 757 + panic!("cache widened solid"); 758 + }; 759 + 760 + assert_ne!( 761 + sketch_before, sketch_after, 762 + "a width edit must change the sketch file" 763 + ); 764 + assert_eq!( 765 + document_before, document_after, 766 + "a sketch-data edit must not churn document.ron" 767 + ); 768 + let Ok(raw) = std::fs::read(folder.sketch_path(sid)) else { 769 + panic!("read sketch bytes"); 770 + }; 771 + assert!( 772 + std::str::from_utf8(&raw).is_ok() && !raw.contains(&0u8), 773 + "sketch file stays text, no binary noise" 774 + ); 775 + 776 + assert_ne!(hash_before, hash_after, "wider box content-addresses anew"); 777 + assert!( 778 + folder.blob_path(hash_before, BlobKind::BREP).exists(), 779 + "previous geometry blob survives content-addressing" 780 + ); 781 + assert!( 782 + folder.blob_path(hash_after, BlobKind::BREP).exists(), 783 + "new geometry blob is written" 784 + ); 785 + assert_eq!( 786 + face_labels(&solid_before), 787 + face_labels(&solid_after), 788 + "a width change preserves face labels, so blob churn is geometry-only" 789 + ); 677 790 } 678 791 679 792 #[test]
+26 -4
crates/bone-document/tests/folder_snapshots.rs
··· 1 1 use bone_document::{ 2 - DimensionKind, Document, DocumentFolder, DocumentHeader, Sketch, SketchDimension, SketchEdit, 3 - SketchEntity, SketchFile, SketchRelation, save, to_string, 2 + DimensionKind, Document, DocumentFolder, DocumentHeader, ExtrudeFile, Sketch, SketchDimension, 3 + SketchEdit, SketchEntity, SketchFile, SketchRelation, save, to_string, 4 + }; 5 + use bone_kernel::{ 6 + ExtrudeDirection, ExtrudeEndCondition, ExtrudeFeature, ExtrudeSense, MergeResult, 4 7 }; 5 8 use bone_types::{ 6 - Angle, DocumentId, Length, Point2, Point3, SketchEntityId, SketchId, SketchPlaneBasis, 7 - Tolerance, UnitVec3, degree, millimeter, 9 + Angle, DocumentId, Length, Point2, Point3, PositiveLength, SketchEntityId, SketchId, 10 + SketchPlaneBasis, Tolerance, UnitVec3, degree, millimeter, 8 11 }; 9 12 use slotmap::KeyData; 10 13 use tempfile::{TempDir, tempdir}; ··· 193 196 let file = SketchFile::new(build_full_sketch()); 194 197 let ron = assert_ron(&file); 195 198 insta::assert_snapshot!("sketch_file", ron); 199 + } 200 + 201 + #[test] 202 + fn extrude_file_ron_surface() { 203 + let Ok(depth) = PositiveLength::new(mm(10.0)) else { 204 + panic!("positive depth"); 205 + }; 206 + let file = ExtrudeFile::new(ExtrudeFeature { 207 + sketch: sketch_id(7), 208 + direction: ExtrudeDirection::Normal { 209 + sense: ExtrudeSense::Forward, 210 + }, 211 + end_condition: ExtrudeEndCondition::Blind { depth }, 212 + draft: None, 213 + thin_wall: None, 214 + merge_result: MergeResult::Merge, 215 + }); 216 + let ron = assert_ron(&file); 217 + insta::assert_snapshot!("extrude_file", ron); 196 218 } 197 219 198 220 #[test]
+31
crates/bone-document/tests/snapshots/folder_snapshots__extrude_file.snap
··· 1 + --- 2 + source: crates/bone-document/tests/folder_snapshots.rs 3 + expression: ron 4 + --- 5 + #![enable(unwrap_newtypes)] 6 + #![enable(implicit_some)] 7 + #![enable(explicit_struct_names)] 8 + ExtrudeFile( 9 + schema: SchemaHeader( 10 + name: "bone-document", 11 + version: SchemaVersion( 12 + major: 1, 13 + minor: 1, 14 + ), 15 + ), 16 + feature: ExtrudeFeature( 17 + sketch: SerKey( 18 + idx: 7, 19 + version: 1, 20 + ), 21 + direction: Normal( 22 + sense: Forward, 23 + ), 24 + end_condition: Blind( 25 + depth: 0.01, 26 + ), 27 + draft: None, 28 + thin_wall: None, 29 + merge_result: Merge, 30 + ), 31 + )
-1
crates/bone-document/tests/snapshots/folder_snapshots__folder_listing.snap
··· 4 4 --- 5 5 .gitattributes 6 6 .gitignore 7 - blobs/ 8 7 caches/ 9 8 caches/.gitignore 10 9 caches/CACHEDIR.TAG