forked from 1b-agent/opencadstudio-upstream
General improvements on API
This commit is contained in:
1 parent
56e09cd3f5
commit
55f61a4327
30 files changed
+1820
-2565
No files matched your search
@@ -1,5 +1,5 @@
|
||||
//! Unit tests over an in-memory mock `DocApiBackend`: no host.
|
||||
//! Covers the §9 examples: per-op atomicity, boolean erase_sources, OpGroup
|
||||
//! Unit tests over an in-memory mock `DocApiBackend` (plan §11.1): no host.
|
||||
//! Covers the §9 examples: per-op atomicity, boolean erase_sources, OpGroup
|
||||
//! compensation, bulk all-or-nothing, revision bumps, read-only query batching,
|
||||
//! and stale-handle `UnknownId` semantics.
|
||||
|
||||
@@ -17,30 +17,15 @@ use ocs_doc_api::{
|
||||
fn curve_bounds(spec: &Curve2Spec) -> Aabb {
|
||||
match spec {
|
||||
Curve2Spec::Line { start, end } => Aabb {
|
||||
min: [
|
||||
start[0].min(end[0]),
|
||||
start[1].min(end[1]),
|
||||
start[2].min(end[2]),
|
||||
],
|
||||
max: [
|
||||
start[0].max(end[0]),
|
||||
start[1].max(end[1]),
|
||||
start[2].max(end[2]),
|
||||
],
|
||||
min: [start[0].min(end[0]), start[1].min(end[1]), start[2].min(end[2])],
|
||||
max: [start[0].max(end[0]), start[1].max(end[1]), start[2].max(end[2])],
|
||||
},
|
||||
Curve2Spec::Circle { centre, radius } | Curve2Spec::Arc { centre, radius, .. } => Aabb {
|
||||
min: [centre[0] - radius, centre[1] - radius, centre[2]],
|
||||
max: [centre[0] + radius, centre[1] + radius, centre[2]],
|
||||
},
|
||||
Curve2Spec::Point { position } => Aabb {
|
||||
min: *position,
|
||||
max: *position,
|
||||
},
|
||||
Curve2Spec::Polyline { points, .. }
|
||||
| Curve2Spec::Spline {
|
||||
control_points: points,
|
||||
..
|
||||
} => {
|
||||
Curve2Spec::Point { position } => Aabb { min: *position, max: *position },
|
||||
Curve2Spec::Polyline { points, .. } | Curve2Spec::Spline { control_points: points, .. } => {
|
||||
let mut min = [f64::INFINITY; 3];
|
||||
let mut max = [f64::NEG_INFINITY; 3];
|
||||
for p in points {
|
||||
@@ -51,28 +36,17 @@ fn curve_bounds(spec: &Curve2Spec) -> Aabb {
|
||||
}
|
||||
Aabb { min, max }
|
||||
}
|
||||
Curve2Spec::Ellipse {
|
||||
centre, major_axis, ..
|
||||
} => {
|
||||
let r = (major_axis[0] * major_axis[0]
|
||||
+ major_axis[1] * major_axis[1]
|
||||
+ major_axis[2] * major_axis[2])
|
||||
.sqrt();
|
||||
Aabb {
|
||||
min: [centre[0] - r, centre[1] - r, centre[2] - r],
|
||||
max: [centre[0] + r, centre[1] + r, centre[2] + r],
|
||||
}
|
||||
Curve2Spec::Ellipse { centre, major_axis, .. } => {
|
||||
let r = (major_axis[0] * major_axis[0] + major_axis[1] * major_axis[1] + major_axis[2] * major_axis[2]).sqrt();
|
||||
Aabb { min: [centre[0] - r, centre[1] - r, centre[2] - r], max: [centre[0] + r, centre[1] + r, centre[2] + r] }
|
||||
}
|
||||
Curve2Spec::Ray { origin, .. } | Curve2Spec::XLine { origin, .. } => Aabb {
|
||||
min: *origin,
|
||||
max: *origin,
|
||||
},
|
||||
Curve2Spec::Ray { origin, .. } | Curve2Spec::XLine { origin, .. } => Aabb { min: *origin, max: *origin },
|
||||
}
|
||||
}
|
||||
|
||||
/// In-memory mock backend: HashMap<ObjectId, Body> + a tiny entity table.
|
||||
/// Tracks undo steps + revision bumps to assert per-op semantics.
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Default)]
|
||||
struct MockBackend {
|
||||
next_id: u64,
|
||||
revision: u64,
|
||||
@@ -121,35 +95,6 @@ impl DocApiBackend for MockBackend {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn create_many(&mut self, specs: &[ocs_doc_api::EntitySpec]) -> ApiResult<Vec<ObjectId>> {
|
||||
let mut next = self.clone();
|
||||
let ids = specs
|
||||
.iter()
|
||||
.map(|spec| match spec {
|
||||
ocs_doc_api::EntitySpec::Solid(spec) => {
|
||||
next.store_solid(&ocs_doc_api::geom::make_solid(spec)?)
|
||||
}
|
||||
ocs_doc_api::EntitySpec::Curve(spec) => {
|
||||
ocs_doc_api::convert::curve_spec_to_entity(spec)?;
|
||||
next.add_curve(spec)
|
||||
}
|
||||
})
|
||||
.collect::<ApiResult<Vec<_>>>()?;
|
||||
next.push_undo("CreateMany");
|
||||
next.finalize_op();
|
||||
*self = next;
|
||||
Ok(ids)
|
||||
}
|
||||
fn transform_many(&mut self, ids: &[ObjectId], placement: &PlacementSpec) -> ApiResult<()> {
|
||||
let mut next = self.clone();
|
||||
for &id in ids {
|
||||
next.transform_entity(id, placement)?;
|
||||
}
|
||||
next.push_undo("TransformMany");
|
||||
next.finalize_op();
|
||||
*self = next;
|
||||
Ok(())
|
||||
}
|
||||
fn add_curve(&mut self, spec: &Curve2Spec) -> ApiResult<ObjectId> {
|
||||
let kind = match spec {
|
||||
Curve2Spec::Line { .. } => "Line",
|
||||
@@ -174,8 +119,7 @@ impl DocApiBackend for MockBackend {
|
||||
}
|
||||
fn add_viewport(&mut self, spec: &ocs_doc_api::ops::ViewportSpec) -> ApiResult<ObjectId> {
|
||||
let id = self.alloc("Viewport");
|
||||
self.viewport_views
|
||||
.insert(id, (spec.view_target, spec.view_height));
|
||||
self.viewport_views.insert(id, (spec.view_target, spec.view_height));
|
||||
Ok(id)
|
||||
}
|
||||
fn add_text(&mut self, spec: &ocs_doc_api::ops::TextSpec) -> ApiResult<ObjectId> {
|
||||
@@ -197,27 +141,17 @@ impl DocApiBackend for MockBackend {
|
||||
}
|
||||
}
|
||||
fn text_content(&self, id: ObjectId) -> ApiResult<String> {
|
||||
self.text_values
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or(ApiError::UnknownId(id))
|
||||
self.text_values.get(&id).cloned().ok_or(ApiError::UnknownId(id))
|
||||
}
|
||||
fn add_hatch(&mut self, spec: &ocs_doc_api::ops::HatchSpec) -> ApiResult<ObjectId> {
|
||||
let id = self.alloc("Hatch");
|
||||
self.hatch_boundaries
|
||||
.insert(id, vec![spec.boundary.clone()]);
|
||||
self.hatch_boundaries.insert(id, vec![spec.boundary.clone()]);
|
||||
Ok(id)
|
||||
}
|
||||
fn hatch_boundary(&self, id: ObjectId) -> ApiResult<Vec<Vec<[f64; 2]>>> {
|
||||
self.hatch_boundaries
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or(ApiError::UnknownId(id))
|
||||
self.hatch_boundaries.get(&id).cloned().ok_or(ApiError::UnknownId(id))
|
||||
}
|
||||
fn add_dimension_linear(
|
||||
&mut self,
|
||||
spec: &ocs_doc_api::ops::DimensionSpec,
|
||||
) -> ApiResult<ObjectId> {
|
||||
fn add_dimension_linear(&mut self, spec: &ocs_doc_api::ops::DimensionSpec) -> ApiResult<ObjectId> {
|
||||
let id = self.alloc("Dimension");
|
||||
let d = ((spec.first_point[0] - spec.second_point[0]).powi(2)
|
||||
+ (spec.first_point[1] - spec.second_point[1]).powi(2)
|
||||
@@ -226,54 +160,30 @@ impl DocApiBackend for MockBackend {
|
||||
self.dimension_measurements.insert(id, d);
|
||||
Ok(id)
|
||||
}
|
||||
fn add_dimension_radius(
|
||||
&mut self,
|
||||
spec: &ocs_doc_api::ops::DimensionRadialSpec,
|
||||
) -> ApiResult<ObjectId> {
|
||||
fn add_dimension_radius(&mut self, spec: &ocs_doc_api::ops::DimensionRadialSpec) -> ApiResult<ObjectId> {
|
||||
let id = self.alloc("Dimension");
|
||||
let d = ((spec.center[0] - spec.point[0]).powi(2)
|
||||
+ (spec.center[1] - spec.point[1]).powi(2)
|
||||
+ (spec.center[2] - spec.point[2]).powi(2))
|
||||
.sqrt();
|
||||
let d = ((spec.center[0] - spec.point[0]).powi(2) + (spec.center[1] - spec.point[1]).powi(2) + (spec.center[2] - spec.point[2]).powi(2)).sqrt();
|
||||
self.dimension_measurements.insert(id, d);
|
||||
Ok(id)
|
||||
}
|
||||
fn add_dimension_diameter(
|
||||
&mut self,
|
||||
spec: &ocs_doc_api::ops::DimensionRadialSpec,
|
||||
) -> ApiResult<ObjectId> {
|
||||
fn add_dimension_diameter(&mut self, spec: &ocs_doc_api::ops::DimensionRadialSpec) -> ApiResult<ObjectId> {
|
||||
self.add_dimension_radius(spec)
|
||||
}
|
||||
fn add_dimension_angular(
|
||||
&mut self,
|
||||
_spec: &ocs_doc_api::ops::DimensionAngularSpec,
|
||||
) -> ApiResult<ObjectId> {
|
||||
fn add_dimension_angular(&mut self, _spec: &ocs_doc_api::ops::DimensionAngularSpec) -> ApiResult<ObjectId> {
|
||||
let id = self.alloc("Dimension");
|
||||
self.dimension_measurements.insert(id, 90.0); // mock: 90 degrees
|
||||
Ok(id)
|
||||
}
|
||||
fn dimension_measurement(&self, id: ObjectId) -> ApiResult<f64> {
|
||||
self.dimension_measurements
|
||||
.get(&id)
|
||||
.copied()
|
||||
.ok_or(ApiError::UnknownId(id))
|
||||
self.dimension_measurements.get(&id).copied().ok_or(ApiError::UnknownId(id))
|
||||
}
|
||||
fn add_attribute_definition(
|
||||
&mut self,
|
||||
spec: &ocs_doc_api::ops::AttributeDefinitionSpec,
|
||||
) -> ApiResult<ObjectId> {
|
||||
fn add_attribute_definition(&mut self, spec: &ocs_doc_api::ops::AttributeDefinitionSpec) -> ApiResult<ObjectId> {
|
||||
if spec.tag.is_empty() {
|
||||
return Err(ApiError::validation(
|
||||
"CreateAttributeDefinition",
|
||||
"empty tag",
|
||||
));
|
||||
return Err(ApiError::validation("CreateAttributeDefinition", "empty tag"));
|
||||
}
|
||||
Ok(self.alloc("AttributeDefinition"))
|
||||
}
|
||||
fn add_dimension_angular2ln(
|
||||
&mut self,
|
||||
_spec: &ocs_doc_api::ops::DimensionAngularSpec,
|
||||
) -> ApiResult<ObjectId> {
|
||||
fn add_dimension_angular2ln(&mut self, _spec: &ocs_doc_api::ops::DimensionAngularSpec) -> ApiResult<ObjectId> {
|
||||
let id = self.alloc("Dimension");
|
||||
self.dimension_measurements.insert(id, 90.0); // mock: 90 degrees
|
||||
Ok(id)
|
||||
@@ -281,10 +191,7 @@ impl DocApiBackend for MockBackend {
|
||||
fn add_table(&mut self, spec: &ocs_doc_api::ops::TableSpec) -> ApiResult<ObjectId> {
|
||||
let cols = spec.data.first().map(|r| r.len()).unwrap_or(0);
|
||||
if spec.data.is_empty() || cols == 0 || spec.data.iter().any(|r| r.len() != cols) {
|
||||
return Err(ApiError::validation(
|
||||
"CreateTable",
|
||||
"table needs a non-empty rectangular grid",
|
||||
));
|
||||
return Err(ApiError::validation("CreateTable", "table needs a non-empty rectangular grid"));
|
||||
}
|
||||
Ok(self.alloc("Table"))
|
||||
}
|
||||
@@ -301,24 +208,15 @@ impl DocApiBackend for MockBackend {
|
||||
Ok(())
|
||||
}
|
||||
fn attributes(&self, id: ObjectId) -> ApiResult<Vec<(String, String)>> {
|
||||
self.attributes_store
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or(ApiError::UnknownId(id))
|
||||
self.attributes_store.get(&id).cloned().ok_or(ApiError::UnknownId(id))
|
||||
}
|
||||
fn block_entities(&self, _block_name: &str) -> ApiResult<Vec<EntityView>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
fn add_raster_image(
|
||||
&mut self,
|
||||
_spec: &ocs_doc_api::ops::RasterImageSpec,
|
||||
) -> ApiResult<ObjectId> {
|
||||
fn add_raster_image(&mut self, _spec: &ocs_doc_api::ops::RasterImageSpec) -> ApiResult<ObjectId> {
|
||||
Ok(self.alloc("RasterImage"))
|
||||
}
|
||||
fn loft(
|
||||
&mut self,
|
||||
sections: &[(cadkernel::space::Plane, Vec<cadkernel::geom2d::Curve>)],
|
||||
) -> ApiResult<ObjectId> {
|
||||
fn loft(&mut self, sections: &[Vec<cadkernel::geom2d::Curve>]) -> ApiResult<ObjectId> {
|
||||
if sections.len() < 2 {
|
||||
return Err(ApiError::validation("Loft", "loft needs >= 2 profiles"));
|
||||
}
|
||||
@@ -327,12 +225,7 @@ impl DocApiBackend for MockBackend {
|
||||
.ok_or_else(|| ApiError::geometry(GeometryErrorKind::InvalidInput, "make cuboid"))?;
|
||||
self.store_solid(&body)
|
||||
}
|
||||
fn set_viewport_view(
|
||||
&mut self,
|
||||
id: ObjectId,
|
||||
view_target: [f64; 3],
|
||||
view_height: f64,
|
||||
) -> ApiResult<()> {
|
||||
fn set_viewport_view(&mut self, id: ObjectId, view_target: [f64; 3], view_height: f64) -> ApiResult<()> {
|
||||
if !self.entity_exists(id) {
|
||||
return Err(ApiError::UnknownId(id));
|
||||
}
|
||||
@@ -340,10 +233,7 @@ impl DocApiBackend for MockBackend {
|
||||
Ok(())
|
||||
}
|
||||
fn viewport_view(&self, id: ObjectId) -> ApiResult<([f64; 3], f64)> {
|
||||
self.viewport_views
|
||||
.get(&id)
|
||||
.copied()
|
||||
.ok_or(ApiError::UnknownId(id))
|
||||
self.viewport_views.get(&id).copied().ok_or(ApiError::UnknownId(id))
|
||||
}
|
||||
fn add_vertex(&mut self, id: ObjectId, _at: usize, _point: [f64; 3]) -> ApiResult<()> {
|
||||
if self.entity_exists(id) {
|
||||
@@ -359,10 +249,7 @@ impl DocApiBackend for MockBackend {
|
||||
fn get_entity(&mut self, id: ObjectId) -> ApiResult<EntityView> {
|
||||
let kind = self.kinds.get(&id).ok_or(ApiError::UnknownId(id))?.clone();
|
||||
let bounds = self.bodies.get(&id).and_then(|b| {
|
||||
cadkernel::brep::body_bounds(b).map(|bb| Aabb {
|
||||
min: bb.min,
|
||||
max: bb.max,
|
||||
})
|
||||
cadkernel::brep::body_bounds(b).map(|bb| Aabb { min: bb.min, max: bb.max })
|
||||
});
|
||||
Ok(EntityView { id, kind, bounds })
|
||||
}
|
||||
@@ -377,9 +264,8 @@ impl DocApiBackend for MockBackend {
|
||||
z_axis: placement.z_axis,
|
||||
origin: placement.origin,
|
||||
};
|
||||
let moved = cadkernel::brep::transform(&body, &place).ok_or_else(|| {
|
||||
ApiError::geometry(GeometryErrorKind::InvalidInput, "transform failed")
|
||||
})?;
|
||||
let moved = cadkernel::brep::transform(&body, &place)
|
||||
.ok_or_else(|| ApiError::geometry(GeometryErrorKind::InvalidInput, "transform failed"))?;
|
||||
self.bodies.insert(id, moved);
|
||||
}
|
||||
Ok(())
|
||||
@@ -396,10 +282,7 @@ impl DocApiBackend for MockBackend {
|
||||
let body = self.body(id)?;
|
||||
let bb = cadkernel::brep::body_bounds(body)
|
||||
.ok_or_else(|| ApiError::geometry(GeometryErrorKind::Empty, "no bounds"))?;
|
||||
Ok(Aabb {
|
||||
min: bb.min,
|
||||
max: bb.max,
|
||||
})
|
||||
Ok(Aabb { min: bb.min, max: bb.max })
|
||||
}
|
||||
fn centroid(&mut self, id: ObjectId) -> ApiResult<[f64; 3]> {
|
||||
let body = self.body(id)?;
|
||||
@@ -422,10 +305,9 @@ impl DocApiBackend for MockBackend {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn api() -> (DocApi, Arc<ocs_doc_api::transport::InProcess<MockBackend>>) {
|
||||
let tp = Arc::new(ocs_doc_api::transport::InProcess::new(
|
||||
MockBackend::default(),
|
||||
));
|
||||
let tp = Arc::new(ocs_doc_api::transport::InProcess::new(MockBackend::default()));
|
||||
(DocApi::connect(tp.clone(), 0), tp)
|
||||
}
|
||||
|
||||
@@ -436,26 +318,13 @@ fn cuboid_intersect_cuboid_atomic_ops_and_revision() {
|
||||
let mut grp = ocs_doc_api::OpGroup::new();
|
||||
|
||||
let rev0 = doc.revision().unwrap();
|
||||
// Two overlapping boxes — the kernel's own boolean test case (boolean.rs pair()).
|
||||
let block = grp
|
||||
.track(
|
||||
doc.solids()
|
||||
.create_cuboid([0.0, 0.0, 0.0], [10.0, 10.0, 10.0]),
|
||||
)
|
||||
.unwrap();
|
||||
let other = grp
|
||||
.track(
|
||||
doc.solids()
|
||||
.create_cuboid([5.0, 5.0, 5.0], [10.0, 10.0, 10.0]),
|
||||
)
|
||||
.unwrap();
|
||||
// Two overlapping boxes — the kernel's own boolean test case (boolean.rs pair()).
|
||||
let block = grp.track(doc.solids().create_cuboid([0.0, 0.0, 0.0], [10.0, 10.0, 10.0])).unwrap();
|
||||
let other = grp.track(doc.solids().create_cuboid([5.0, 5.0, 5.0], [10.0, 10.0, 10.0])).unwrap();
|
||||
assert_eq!(doc.revision().unwrap().as_u64(), rev0.as_u64() + 2);
|
||||
|
||||
let lens = match block.intersect(&other) {
|
||||
Ok(l) => {
|
||||
grp.commit();
|
||||
l
|
||||
}
|
||||
Ok(l) => { grp.commit(); l }
|
||||
Err(e) => panic!("intersect failed: {e}"),
|
||||
};
|
||||
assert_eq!(doc.revision().unwrap().as_u64(), rev0.as_u64() + 3);
|
||||
@@ -468,11 +337,8 @@ fn cuboid_intersect_cuboid_atomic_ops_and_revision() {
|
||||
.unwrap();
|
||||
let bb = res.bounds(0).unwrap();
|
||||
let vol = res.volume(1).unwrap();
|
||||
// Intersection of [0,10]³ ∩ [5,15]³ = [5,10]³ → bounds + volume 125.
|
||||
assert!(
|
||||
(bb.min[0] - 5.0).abs() < 1e-6 && (bb.max[0] - 10.0).abs() < 1e-6,
|
||||
"bounds {bb:?}"
|
||||
);
|
||||
// Intersection of [0,10]³ ∩ [5,15]³ = [5,10]³ → bounds + volume 125.
|
||||
assert!((bb.min[0] - 5.0).abs() < 1e-6 && (bb.max[0] - 10.0).abs() < 1e-6, "bounds {bb:?}");
|
||||
assert!(vol > 0.0, "intersection volume must be positive");
|
||||
}
|
||||
|
||||
@@ -480,14 +346,8 @@ fn cuboid_intersect_cuboid_atomic_ops_and_revision() {
|
||||
fn boolean_erase_sources_removes_second_solid() {
|
||||
let (api, tp) = api();
|
||||
let doc = api.document(api.active_tab());
|
||||
let a = doc
|
||||
.solids()
|
||||
.create_cuboid([0.0, 0.0, 0.0], [10.0; 3])
|
||||
.unwrap();
|
||||
let b = doc
|
||||
.solids()
|
||||
.create_cuboid([5.0, 5.0, 5.0], [10.0; 3])
|
||||
.unwrap();
|
||||
let a = doc.solids().create_cuboid([0.0, 0.0, 0.0], [10.0; 3]).unwrap();
|
||||
let b = doc.solids().create_cuboid([5.0, 5.0, 5.0], [10.0; 3]).unwrap();
|
||||
let result = a.union(&b).unwrap();
|
||||
let backend = tp.backend();
|
||||
assert!(backend.entity_exists(a.id()));
|
||||
@@ -508,12 +368,8 @@ fn opgroup_compensate_deletes_created() {
|
||||
let (api, tp) = api();
|
||||
let doc = api.document(api.active_tab());
|
||||
let mut grp = ocs_doc_api::OpGroup::new();
|
||||
let a = grp
|
||||
.track(doc.solids().create_cuboid([0.0, 0.0, 0.0], [10.0; 3]))
|
||||
.unwrap();
|
||||
let b = grp
|
||||
.track(doc.solids().create_sphere([5.0, 5.0, 5.0], 4.0))
|
||||
.unwrap();
|
||||
let a = grp.track(doc.solids().create_cuboid([0.0, 0.0, 0.0], [10.0; 3])).unwrap();
|
||||
let b = grp.track(doc.solids().create_sphere([5.0, 5.0, 5.0], 4.0)).unwrap();
|
||||
grp.compensate(&doc).unwrap();
|
||||
let backend = tp.backend();
|
||||
assert!(!backend.entity_exists(a.id()));
|
||||
@@ -537,10 +393,7 @@ fn bulk_create_many_one_undo_step() {
|
||||
fn transform_many_with_stale_id_fails_and_applies_nothing() {
|
||||
let (api, tp) = api();
|
||||
let doc = api.document(api.active_tab());
|
||||
let a = doc
|
||||
.solids()
|
||||
.create_cuboid([0.0, 0.0, 0.0], [10.0; 3])
|
||||
.unwrap();
|
||||
let a = doc.solids().create_cuboid([0.0, 0.0, 0.0], [10.0; 3]).unwrap();
|
||||
let rev_before = doc.revision().unwrap().as_u64();
|
||||
let stale = ObjectId::from_u64(424_242);
|
||||
let err = doc
|
||||
@@ -558,9 +411,7 @@ fn assert_revision_guard() {
|
||||
let doc = api.document(api.active_tab());
|
||||
let rev = doc.revision().unwrap();
|
||||
doc.assert_revision(rev).unwrap();
|
||||
doc.solids()
|
||||
.create_cuboid([0.0, 0.0, 0.0], [1.0; 3])
|
||||
.unwrap();
|
||||
doc.solids().create_cuboid([0.0, 0.0, 0.0], [1.0; 3]).unwrap();
|
||||
assert!(doc.assert_revision(rev).is_err());
|
||||
}
|
||||
|
||||
@@ -568,10 +419,7 @@ fn assert_revision_guard() {
|
||||
fn failed_boolean_leaves_inputs_live_and_no_undo() {
|
||||
let (api, tp) = api();
|
||||
let doc = api.document(api.active_tab());
|
||||
let a = doc
|
||||
.solids()
|
||||
.create_cuboid([0.0, 0.0, 0.0], [10.0; 3])
|
||||
.unwrap();
|
||||
let a = doc.solids().create_cuboid([0.0, 0.0, 0.0], [10.0; 3]).unwrap();
|
||||
// `b` is stale: boolean fails before push_undo -> no mutation, no undo step.
|
||||
let stale_solid = {
|
||||
// Build a Solid handle to a non-existent id via Entity downcast path.
|
||||
@@ -587,25 +435,3 @@ fn failed_boolean_leaves_inputs_live_and_no_undo() {
|
||||
assert!(backend.entity_exists(a.id()));
|
||||
assert_eq!(backend.revision, rev_before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_handles_reject_foreign_sessions_and_tabs() {
|
||||
let first = DocApi::in_process(MockBackend::default(), 10);
|
||||
let second = DocApi::in_process(MockBackend::default(), 10);
|
||||
let doc = first.document(10);
|
||||
let foreign = second
|
||||
.document(10)
|
||||
.curves()
|
||||
.create_circle([0.0; 3], 2.0)
|
||||
.unwrap();
|
||||
assert!(doc.solids().extrude(&foreign, [0.0, 0.0, 2.0]).is_err());
|
||||
assert!(doc
|
||||
.solids()
|
||||
.loft(&[foreign.clone(), foreign.clone()])
|
||||
.is_err());
|
||||
assert!(doc.query_batch(|q| q.bounds(&foreign)).is_err());
|
||||
let mut group = ocs_doc_api::OpGroup::new();
|
||||
group.track(Ok(foreign)).unwrap();
|
||||
assert!(group.compensate(&doc).is_err());
|
||||
assert!(first.document(11).curves().create_point([0.0; 3]).is_err());
|
||||
}
|
||||
Reference in new issue
Block a user