Skip to main content

axioval_engine/
boundary_coverage.rs

1//! Space-boundary coverage: how much of a space's body surface the space
2//! boundaries its source declares for it cover, where they leave gaps and
3//! where they overlap.
4//!
5//! ADR 0004: this seam measures. Whether a space's boundaries are complete
6//! enough, or overlap too much, is a rule's judgement over these areas.
7//!
8//! A space boundary is the source's statement that a surface bounds a
9//! space: its identity (the stating relationship, source-qualified), the
10//! element on its other side when the source names one, and its connection
11//! surface. Which boundaries a space has is a source fact the host
12//! registers, never the rule's selection; a boundary whose surface the host
13//! could not read leaves the space unmeasured, never measured without it.
14//!
15//! Areas lie on the body's surface: each boundary surface lying on a face
16//! plane of the body, within the request's `plane_tolerance`, counts on that
17//! plane; one lying on no face plane is reported
18//! ([`BoundaryPlacement::OffSurface`]) and covers nothing. The covered share
19//! is derived here from the covered and surface areas, never accepted from
20//! an adapter. The evidence is exact only when every input is exact and
21//! planar and nothing was rounded on the way; then every interval is a point.
22
23use std::sync::Arc;
24
25use axioval_ir::{Evidence, ObjectId};
26use thiserror::Error;
27
28/// Failure to measure a space's boundary coverage.
29#[derive(Clone, Debug, Error, PartialEq, Eq)]
30pub enum BoundaryCoverageError {
31    /// The service holds no space boundaries for this object: it is not a
32    /// space the host registered.
33    #[error("no space boundaries registered for `{0}`")]
34    UnknownSpace(ObjectId),
35    /// The space occupies no volume, so it has no surface to cover.
36    #[error("space `{0}` has no body")]
37    NoBody(ObjectId),
38    /// The space's body or one of its boundary surfaces could not be
39    /// measured, for example a surface the host could not read, a curved
40    /// body or an overlay that cannot be computed.
41    #[error("boundary coverage unavailable: {0}")]
42    Unavailable(String),
43    /// The request is malformed.
44    #[error("invalid boundary coverage request: {0}")]
45    InvalidRequest(String),
46    /// The service does not measure boundary coverage.
47    #[error("the geometry service does not measure space-boundary coverage")]
48    Unsupported,
49    /// The answer is malformed, inconsistent or bound to another request.
50    #[error("boundary coverage measurement is invalid")]
51    InvalidMeasurement,
52}
53
54/// How much of `space`'s body surface its declared boundaries cover.
55#[derive(Clone, Debug, PartialEq)]
56pub struct BoundaryCoverageRequest {
57    space: ObjectId,
58    plane_tolerance: f64,
59}
60
61impl BoundaryCoverageRequest {
62    /// Coverage of `space`, counting a boundary surface on a face plane when
63    /// every point of it lies within `plane_tolerance_metres` of the plane.
64    ///
65    /// # Errors
66    ///
67    /// Refuses a negative or non-finite tolerance.
68    pub fn try_new(
69        space: ObjectId,
70        plane_tolerance_metres: f64,
71    ) -> Result<Self, BoundaryCoverageError> {
72        if !plane_tolerance_metres.is_finite() || plane_tolerance_metres < 0.0 {
73            return Err(BoundaryCoverageError::InvalidRequest(format!(
74                "plane tolerance must be a finite length of at least zero, not \
75                 {plane_tolerance_metres}"
76            )));
77        }
78        Ok(Self {
79            space,
80            plane_tolerance: plane_tolerance_metres,
81        })
82    }
83
84    /// The space whose surface is measured.
85    #[must_use]
86    pub fn space(&self) -> &ObjectId {
87        &self.space
88    }
89
90    /// How far from a face plane a boundary surface may lie and still count
91    /// on it, in metres.
92    #[must_use]
93    pub fn plane_tolerance_metres(&self) -> f64 {
94        self.plane_tolerance
95    }
96}
97
98/// An area on a surface, in square metres, known to lie in `[lower, upper]`.
99#[derive(Clone, Copy, Debug, PartialEq)]
100pub struct SurfaceAreaInterval {
101    lower: f64,
102    upper: f64,
103}
104
105impl SurfaceAreaInterval {
106    /// An area in `[lower, upper]`.
107    ///
108    /// # Errors
109    ///
110    /// Refuses non-finite or negative bounds and reversed ones.
111    pub fn try_new(lower: f64, upper: f64) -> Result<Self, BoundaryCoverageError> {
112        if !lower.is_finite() || !upper.is_finite() || lower < 0.0 || lower > upper {
113            return Err(BoundaryCoverageError::InvalidMeasurement);
114        }
115        Ok(Self { lower, upper })
116    }
117
118    /// An area known exactly.
119    ///
120    /// # Errors
121    ///
122    /// Refuses a non-finite or negative area.
123    pub fn exact(square_metres: f64) -> Result<Self, BoundaryCoverageError> {
124        Self::try_new(square_metres, square_metres)
125    }
126
127    /// The smallest the area can be.
128    #[must_use]
129    pub fn lower_square_metres(&self) -> f64 {
130        self.lower
131    }
132
133    /// The largest the area can be.
134    #[must_use]
135    pub fn upper_square_metres(&self) -> f64 {
136        self.upper
137    }
138
139    /// Whether the interval is a single value.
140    #[must_use]
141    #[allow(clippy::float_cmp)]
142    pub fn is_point(&self) -> bool {
143        self.lower == self.upper
144    }
145}
146
147/// A share in `[0, 1]`, known to lie in `[lower, upper]`.
148#[derive(Clone, Copy, Debug, PartialEq)]
149pub struct ShareInterval {
150    lower: f64,
151    upper: f64,
152}
153
154impl ShareInterval {
155    /// The smallest the share can be.
156    #[must_use]
157    pub fn lower(&self) -> f64 {
158        self.lower
159    }
160
161    /// The largest the share can be.
162    #[must_use]
163    pub fn upper(&self) -> f64 {
164        self.upper
165    }
166}
167
168/// Where a boundary surface lies against the space's body.
169#[derive(Clone, Copy, Debug, PartialEq)]
170pub enum BoundaryPlacement {
171    /// On the face planes of the body; `area` is the boundary's own area
172    /// there, overlaps with other boundaries and parts beyond the faces
173    /// included.
174    OnSurface {
175        /// The boundary's area projected into the face planes it lies on.
176        area: SurfaceAreaInterval,
177    },
178    /// On no face plane of the body within the tolerance: it covers nothing.
179    OffSurface,
180}
181
182/// One declared boundary of the space, as measured.
183#[derive(Clone, Debug, PartialEq)]
184pub struct MeasuredBoundary {
185    boundary: ObjectId,
186    element: Option<ObjectId>,
187    placement: BoundaryPlacement,
188}
189
190impl MeasuredBoundary {
191    /// `boundary` (the source's boundary identity), bounding the space
192    /// against `element` when the source names one.
193    #[must_use]
194    pub fn new(
195        boundary: ObjectId,
196        element: Option<ObjectId>,
197        placement: BoundaryPlacement,
198    ) -> Self {
199        Self {
200            boundary,
201            element,
202            placement,
203        }
204    }
205
206    /// The boundary's source-qualified identity.
207    #[must_use]
208    pub fn boundary(&self) -> &ObjectId {
209        &self.boundary
210    }
211
212    /// The element on the boundary's other side, when the source names one.
213    #[must_use]
214    pub fn element(&self) -> Option<&ObjectId> {
215        self.element.as_ref()
216    }
217
218    /// Where the boundary lies against the body.
219    #[must_use]
220    pub fn placement(&self) -> BoundaryPlacement {
221        self.placement
222    }
223}
224
225/// Two boundaries covering the same part of the surface.
226#[derive(Clone, Debug, PartialEq)]
227pub struct BoundaryOverlap {
228    first: ObjectId,
229    second: ObjectId,
230    area: SurfaceAreaInterval,
231}
232
233impl BoundaryOverlap {
234    /// `first` and `second` overlapping by `area`; the pair is ordered here,
235    /// so one overlap has one spelling.
236    ///
237    /// # Errors
238    ///
239    /// Refuses a boundary overlapping itself.
240    pub fn try_new(
241        first: ObjectId,
242        second: ObjectId,
243        area: SurfaceAreaInterval,
244    ) -> Result<Self, BoundaryCoverageError> {
245        if first == second {
246            return Err(BoundaryCoverageError::InvalidMeasurement);
247        }
248        let (first, second) = if first < second {
249            (first, second)
250        } else {
251            (second, first)
252        };
253        Ok(Self {
254            first,
255            second,
256            area,
257        })
258    }
259
260    /// The first boundary of the pair, in identity order.
261    #[must_use]
262    pub fn first(&self) -> &ObjectId {
263        &self.first
264    }
265
266    /// The second boundary of the pair, in identity order.
267    #[must_use]
268    pub fn second(&self) -> &ObjectId {
269        &self.second
270    }
271
272    /// The area both cover.
273    #[must_use]
274    pub fn area(&self) -> SurfaceAreaInterval {
275        self.area
276    }
277}
278
279/// The areas a boundary-coverage answer reports, in square metres.
280#[derive(Clone, Copy, Debug, PartialEq)]
281pub struct CoverageAreas {
282    /// The body's surface area.
283    pub surface: SurfaceAreaInterval,
284    /// The part of the surface at least one boundary covers.
285    pub covered: SurfaceAreaInterval,
286    /// The part of the surface no boundary covers.
287    pub uncovered: SurfaceAreaInterval,
288    /// The part of the surface two or more boundaries cover.
289    pub overlap: SurfaceAreaInterval,
290}
291
292/// How much of a space's body surface its declared boundaries cover, bound
293/// to the request it answers.
294#[derive(Clone, Debug, PartialEq)]
295pub struct BoundaryCoverage {
296    request: BoundaryCoverageRequest,
297    areas: CoverageAreas,
298    share: ShareInterval,
299    boundaries: Vec<MeasuredBoundary>,
300    overlaps: Vec<BoundaryOverlap>,
301    evidence: Evidence,
302}
303
304impl BoundaryCoverage {
305    /// An answer to `request`: the `areas`, every declared boundary of the
306    /// space as measured and the pairs of boundaries that overlap.
307    ///
308    /// The covered share is derived from the covered and surface areas.
309    ///
310    /// # Errors
311    ///
312    /// Refuses an unlocated evidence, a surface that may be empty, covered
313    /// and uncovered areas that cannot add up to the surface, a boundary
314    /// listed twice, an overlap naming a boundary not on the surface, and
315    /// exact evidence over an interval that is not a point.
316    pub fn try_new(
317        request: BoundaryCoverageRequest,
318        areas: CoverageAreas,
319        mut boundaries: Vec<MeasuredBoundary>,
320        mut overlaps: Vec<BoundaryOverlap>,
321        evidence: Evidence,
322    ) -> Result<Self, BoundaryCoverageError> {
323        let invalid = || BoundaryCoverageError::InvalidMeasurement;
324        if evidence.locator.trim().is_empty() {
325            return Err(invalid());
326        }
327        let CoverageAreas {
328            surface,
329            covered,
330            uncovered,
331            overlap,
332        } = areas;
333        if surface.lower <= 0.0 {
334            return Err(invalid());
335        }
336        // Covered and uncovered partition the surface; allow the rounding of
337        // adding two measured areas.
338        let slack = 1e-9 * surface.upper.max(1.0);
339        if covered.lower + uncovered.lower > surface.upper + slack
340            || covered.upper + uncovered.upper < surface.lower - slack
341            || covered.lower > surface.upper
342            || uncovered.lower > surface.upper
343            || overlap.lower > covered.upper + slack
344        {
345            return Err(invalid());
346        }
347        boundaries.sort_by(|a, b| a.boundary.cmp(&b.boundary));
348        if boundaries
349            .windows(2)
350            .any(|pair| pair[0].boundary == pair[1].boundary)
351        {
352            return Err(invalid());
353        }
354        let on_surface = |id: &ObjectId| {
355            boundaries
356                .binary_search_by(|boundary| boundary.boundary.cmp(id))
357                .is_ok_and(|index| {
358                    matches!(
359                        boundaries[index].placement,
360                        BoundaryPlacement::OnSurface { .. }
361                    )
362                })
363        };
364        if overlaps
365            .iter()
366            .any(|pair| !on_surface(&pair.first) || !on_surface(&pair.second))
367        {
368            return Err(invalid());
369        }
370        overlaps.sort_by(|a, b| (&a.first, &a.second).cmp(&(&b.first, &b.second)));
371        if overlaps
372            .windows(2)
373            .any(|pair| (&pair[0].first, &pair[0].second) == (&pair[1].first, &pair[1].second))
374        {
375            return Err(invalid());
376        }
377        if evidence.exact {
378            let points = [surface, covered, uncovered, overlap]
379                .iter()
380                .chain(overlaps.iter().map(|pair| &pair.area))
381                .all(SurfaceAreaInterval::is_point)
382                && boundaries.iter().all(|boundary| match boundary.placement {
383                    BoundaryPlacement::OnSurface { area } => area.is_point(),
384                    BoundaryPlacement::OffSurface => true,
385                });
386            if !points {
387                return Err(invalid());
388            }
389        }
390        let share = ShareInterval {
391            lower: (covered.lower / surface.upper).clamp(0.0, 1.0),
392            upper: (covered.upper / surface.lower).clamp(0.0, 1.0),
393        };
394        Ok(Self {
395            request,
396            areas,
397            share,
398            boundaries,
399            overlaps,
400            evidence,
401        })
402    }
403
404    /// The request this answers.
405    #[must_use]
406    pub fn request(&self) -> &BoundaryCoverageRequest {
407        &self.request
408    }
409
410    /// The measured space.
411    #[must_use]
412    pub fn space(&self) -> &ObjectId {
413        &self.request.space
414    }
415
416    /// The body's surface area.
417    #[must_use]
418    pub fn surface_area(&self) -> SurfaceAreaInterval {
419        self.areas.surface
420    }
421
422    /// The part of the surface at least one boundary covers.
423    #[must_use]
424    pub fn covered_area(&self) -> SurfaceAreaInterval {
425        self.areas.covered
426    }
427
428    /// The part of the surface no boundary covers.
429    #[must_use]
430    pub fn uncovered_area(&self) -> SurfaceAreaInterval {
431        self.areas.uncovered
432    }
433
434    /// The part of the surface two or more boundaries cover.
435    #[must_use]
436    pub fn overlap_area(&self) -> SurfaceAreaInterval {
437        self.areas.overlap
438    }
439
440    /// The covered share of the surface: the covered area over the surface
441    /// area, bounded by the least covered over the largest surface and the
442    /// most covered over the smallest.
443    #[must_use]
444    pub fn covered_share(&self) -> ShareInterval {
445        self.share
446    }
447
448    /// Every declared boundary of the space, in identity order.
449    #[must_use]
450    pub fn boundaries(&self) -> &[MeasuredBoundary] {
451        &self.boundaries
452    }
453
454    /// The boundaries lying on no face plane of the body.
455    pub fn off_surface(&self) -> impl Iterator<Item = &MeasuredBoundary> {
456        self.boundaries
457            .iter()
458            .filter(|boundary| boundary.placement == BoundaryPlacement::OffSurface)
459    }
460
461    /// The pairs of boundaries covering a common part of the surface, in
462    /// identity order.
463    #[must_use]
464    pub fn overlaps(&self) -> &[BoundaryOverlap] {
465        &self.overlaps
466    }
467
468    /// Whether every area is known exactly.
469    #[must_use]
470    pub fn is_exact(&self) -> bool {
471        self.evidence.exact
472    }
473
474    /// Reviewable provenance of the measurement.
475    #[must_use]
476    pub fn evidence(&self) -> &Evidence {
477        &self.evidence
478    }
479}
480
481/// Measures how much of a space's body surface its declared boundaries cover.
482pub trait BoundaryCoverageService: Send + Sync + 'static {
483    /// The coverage `request` asks for. The default refuses: a service that
484    /// does not measure boundaries must never answer with an empty coverage.
485    ///
486    /// # Errors
487    ///
488    /// Returns [`BoundaryCoverageError::Unsupported`] unless implemented.
489    fn measure_boundary_coverage(
490        &self,
491        request: &BoundaryCoverageRequest,
492    ) -> Result<BoundaryCoverage, BoundaryCoverageError> {
493        let _ = request;
494        Err(BoundaryCoverageError::Unsupported)
495    }
496}
497
498/// Registry handle for a [`BoundaryCoverageService`].
499#[derive(Clone)]
500pub struct BoundaryCoverageServiceHandle(Arc<dyn BoundaryCoverageService>);
501
502impl BoundaryCoverageServiceHandle {
503    /// Wraps a trusted boundary-coverage service.
504    #[must_use]
505    pub fn new(service: Arc<dyn BoundaryCoverageService>) -> Self {
506        Self(service)
507    }
508
509    /// The coverage `request` asks for. An answer bound to another request
510    /// is refused.
511    ///
512    /// # Errors
513    ///
514    /// Returns the service's refusal, or
515    /// [`BoundaryCoverageError::InvalidMeasurement`] for an answer to another
516    /// request.
517    pub fn measure_boundary_coverage(
518        &self,
519        request: &BoundaryCoverageRequest,
520    ) -> Result<BoundaryCoverage, BoundaryCoverageError> {
521        let answer = self.0.measure_boundary_coverage(request)?;
522        if answer.request() != request {
523            return Err(BoundaryCoverageError::InvalidMeasurement);
524        }
525        Ok(answer)
526    }
527}
528
529#[cfg(test)]
530mod tests {
531    use super::*;
532    use axioval_ir::SourceId;
533
534    fn source() -> SourceId {
535        SourceId::new("cad", "m").unwrap()
536    }
537
538    fn id(local: &str) -> ObjectId {
539        ObjectId::new(source(), local).unwrap()
540    }
541
542    fn area(lower: f64, upper: f64) -> SurfaceAreaInterval {
543        SurfaceAreaInterval::try_new(lower, upper).unwrap()
544    }
545
546    fn point(value: f64) -> SurfaceAreaInterval {
547        SurfaceAreaInterval::exact(value).unwrap()
548    }
549
550    fn request() -> BoundaryCoverageRequest {
551        BoundaryCoverageRequest::try_new(id("space"), 0.001).unwrap()
552    }
553
554    fn areas(surface: f64, covered: f64, overlap: f64) -> CoverageAreas {
555        CoverageAreas {
556            surface: point(surface),
557            covered: point(covered),
558            uncovered: point(surface - covered),
559            overlap: point(overlap),
560        }
561    }
562
563    fn on(boundary: &str, value: f64) -> MeasuredBoundary {
564        MeasuredBoundary::new(
565            id(boundary),
566            Some(id("wall")),
567            BoundaryPlacement::OnSurface { area: point(value) },
568        )
569    }
570
571    fn exact() -> Evidence {
572        Evidence::exact(source(), "boundary-coverage:space")
573    }
574
575    #[test]
576    fn a_request_needs_a_finite_non_negative_tolerance() {
577        assert!(BoundaryCoverageRequest::try_new(id("space"), 0.0).is_ok());
578        for tolerance in [-0.1, f64::NAN, f64::INFINITY] {
579            assert!(matches!(
580                BoundaryCoverageRequest::try_new(id("space"), tolerance),
581                Err(BoundaryCoverageError::InvalidRequest(_))
582            ));
583        }
584    }
585
586    #[test]
587    fn intervals_refuse_reversed_negative_and_non_finite_bounds() {
588        assert!(SurfaceAreaInterval::try_new(1.0, 2.0).is_ok());
589        for (lower, upper) in [
590            (2.0, 1.0),
591            (-1.0, 1.0),
592            (0.0, f64::INFINITY),
593            (f64::NAN, 1.0),
594        ] {
595            assert_eq!(
596                SurfaceAreaInterval::try_new(lower, upper),
597                Err(BoundaryCoverageError::InvalidMeasurement)
598            );
599        }
600    }
601
602    #[test]
603    fn the_share_is_derived_from_covered_and_surface_areas() {
604        let coverage = BoundaryCoverage::try_new(
605            request(),
606            areas(10.0, 7.5, 0.0),
607            vec![on("b", 5.0), on("a", 2.5)],
608            vec![],
609            exact(),
610        )
611        .unwrap();
612        assert!((coverage.covered_share().lower() - 0.75).abs() < 1e-12);
613        assert!((coverage.covered_share().upper() - 0.75).abs() < 1e-12);
614        assert_eq!(coverage.boundaries()[0].boundary(), &id("a"));
615
616        let approximate = BoundaryCoverage::try_new(
617            request(),
618            CoverageAreas {
619                surface: area(9.0, 10.0),
620                covered: area(4.5, 5.0),
621                uncovered: area(4.5, 5.5),
622                overlap: area(0.0, 0.1),
623            },
624            vec![],
625            vec![],
626            Evidence {
627                exact: false,
628                ..exact()
629            },
630        )
631        .unwrap();
632        assert!((approximate.covered_share().lower() - 0.45).abs() < 1e-12);
633        assert!((approximate.covered_share().upper() - 5.0 / 9.0).abs() < 1e-12);
634    }
635
636    #[test]
637    fn exact_evidence_needs_point_intervals() {
638        let widened = CoverageAreas {
639            surface: area(10.0, 10.1),
640            ..areas(10.0, 5.0, 0.0)
641        };
642        assert_eq!(
643            BoundaryCoverage::try_new(request(), widened, vec![], vec![], exact()),
644            Err(BoundaryCoverageError::InvalidMeasurement)
645        );
646        let widened_boundary = MeasuredBoundary::new(
647            id("a"),
648            None,
649            BoundaryPlacement::OnSurface {
650                area: area(1.0, 1.1),
651            },
652        );
653        assert_eq!(
654            BoundaryCoverage::try_new(
655                request(),
656                areas(10.0, 1.0, 0.0),
657                vec![widened_boundary],
658                vec![],
659                exact()
660            ),
661            Err(BoundaryCoverageError::InvalidMeasurement)
662        );
663    }
664
665    #[test]
666    fn inconsistent_areas_are_refused() {
667        // Covered and uncovered adding up to more than the surface.
668        let over = CoverageAreas {
669            uncovered: point(6.0),
670            ..areas(10.0, 5.0, 0.0)
671        };
672        // An empty surface has no share.
673        let empty = areas(0.0, 0.0, 0.0);
674        // More overlap than coverage.
675        let overlapping = areas(10.0, 1.0, 2.0);
676        for areas in [over, empty, overlapping] {
677            assert_eq!(
678                BoundaryCoverage::try_new(request(), areas, vec![], vec![], exact()),
679                Err(BoundaryCoverageError::InvalidMeasurement)
680            );
681        }
682    }
683
684    #[test]
685    fn boundaries_are_listed_once_and_overlaps_name_boundaries_on_the_surface() {
686        assert_eq!(
687            BoundaryCoverage::try_new(
688                request(),
689                areas(10.0, 5.0, 0.0),
690                vec![on("a", 5.0), on("a", 5.0)],
691                vec![],
692                exact()
693            ),
694            Err(BoundaryCoverageError::InvalidMeasurement)
695        );
696        let off = MeasuredBoundary::new(id("c"), None, BoundaryPlacement::OffSurface);
697        let with = |pair: BoundaryOverlap| {
698            BoundaryCoverage::try_new(
699                request(),
700                areas(10.0, 5.0, 1.0),
701                vec![on("a", 3.0), on("b", 3.0), off.clone()],
702                vec![pair],
703                exact(),
704            )
705        };
706        let coverage =
707            with(BoundaryOverlap::try_new(id("b"), id("a"), point(1.0)).unwrap()).unwrap();
708        assert_eq!(coverage.overlaps()[0].first(), &id("a"));
709        assert_eq!(coverage.off_surface().count(), 1);
710        for pair in [
711            BoundaryOverlap::try_new(id("a"), id("c"), point(1.0)).unwrap(),
712            BoundaryOverlap::try_new(id("a"), id("z"), point(1.0)).unwrap(),
713        ] {
714            assert_eq!(with(pair), Err(BoundaryCoverageError::InvalidMeasurement));
715        }
716        assert_eq!(
717            BoundaryOverlap::try_new(id("a"), id("a"), point(1.0)),
718            Err(BoundaryCoverageError::InvalidMeasurement)
719        );
720    }
721
722    struct Refusing;
723    impl BoundaryCoverageService for Refusing {}
724
725    struct Other;
726    impl BoundaryCoverageService for Other {
727        fn measure_boundary_coverage(
728            &self,
729            _: &BoundaryCoverageRequest,
730        ) -> Result<BoundaryCoverage, BoundaryCoverageError> {
731            BoundaryCoverage::try_new(
732                BoundaryCoverageRequest::try_new(id("space"), 0.5).unwrap(),
733                areas(10.0, 10.0, 0.0),
734                vec![],
735                vec![],
736                exact(),
737            )
738        }
739    }
740
741    #[test]
742    fn the_default_refuses_and_the_handle_binds_answers_to_the_request() {
743        let refusing = BoundaryCoverageServiceHandle::new(Arc::new(Refusing));
744        assert_eq!(
745            refusing.measure_boundary_coverage(&request()),
746            Err(BoundaryCoverageError::Unsupported)
747        );
748        let other = BoundaryCoverageServiceHandle::new(Arc::new(Other));
749        assert_eq!(
750            other.measure_boundary_coverage(&request()),
751            Err(BoundaryCoverageError::InvalidMeasurement)
752        );
753        let same = BoundaryCoverageRequest::try_new(id("space"), 0.5).unwrap();
754        assert!(other.measure_boundary_coverage(&same).is_ok());
755    }
756}