Skip to main content

routing_test_helpers/
lib.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod availability;
6pub mod component_id_index;
7pub mod dictionary;
8pub mod policy;
9pub mod rights;
10pub mod storage;
11pub mod storage_admin;
12
13use ::component_id_index::InstanceId;
14use assert_matches::assert_matches;
15use async_trait::async_trait;
16use camino::Utf8PathBuf;
17use capability_source::{
18    AggregateCapability, AggregateMember, AnonymizedAggregateSource, BuiltinSource,
19    CapabilitySource, ComponentCapability, ComponentSource, FilteredAggregateProviderSource,
20    InternalCapability,
21};
22use cm_config::{
23    AllowlistEntry, AllowlistEntryBuilder, CapabilityAllowlistKey, CapabilityAllowlistSource,
24    DebugCapabilityAllowlistEntry, DebugCapabilityKey,
25};
26use cm_rust::*;
27use cm_rust_testing::*;
28use cm_types::Name;
29use fidl::endpoints::ProtocolMarker;
30use fidl_fuchsia_component as fcomponent;
31use fidl_fuchsia_component_runner as fcrunner;
32use fidl_fuchsia_data as fdata;
33use fidl_fuchsia_io as fio;
34use moniker::{ExtendedMoniker, Moniker};
35use routing::component_instance::ComponentInstanceInterface;
36use routing::debug_route_sandbox_path;
37use routing::error::{RouteVerb, RoutingError};
38use std::collections::HashSet;
39use std::marker::PhantomData;
40use std::path::{Path, PathBuf};
41use std::sync::Arc;
42use zx_status as zx;
43
44/// Construct a capability path for the hippo service.
45pub fn default_service_capability() -> cm_types::Path {
46    "/svc/hippo".parse().unwrap()
47}
48
49/// Construct a capability path for the hippo directory.
50pub fn default_directory_capability() -> cm_types::Path {
51    "/data/hippo".parse().unwrap()
52}
53
54/// Returns an empty component decl for an executable component.
55pub fn default_component_decl() -> ComponentDecl {
56    ComponentDecl::default()
57}
58
59/// Returns an empty component decl set up to have a non-empty program and to use the "test_runner"
60/// runner.
61pub fn component_decl_with_test_runner() -> ComponentDecl {
62    ComponentDecl {
63        program: Some(ProgramDecl {
64            runner: Some(TEST_RUNNER_NAME.parse().unwrap()),
65            info: fdata::Dictionary { entries: Some(vec![]), ..Default::default() },
66        }),
67        ..Default::default()
68    }
69}
70
71/// Same as above but with the component also exposing Binder protocol.
72pub fn component_decl_with_exposed_binder() -> ComponentDecl {
73    ComponentDecl {
74        program: Some(ProgramDecl {
75            runner: Some(TEST_RUNNER_NAME.parse().unwrap()),
76            info: fdata::Dictionary { entries: Some(vec![]), ..Default::default() },
77        }),
78        exposes: Box::from([ExposeBuilder::protocol()
79            .source(ExposeSource::Framework)
80            .name(fcomponent::BinderMarker::DEBUG_NAME)
81            .build()]),
82        ..Default::default()
83    }
84}
85
86#[derive(Debug, PartialEq, Clone)]
87pub enum ExpectedResult {
88    Ok,
89    Err(zx::Status),
90    ErrWithNoEpitaph,
91}
92
93#[derive(Debug, Clone, Eq, PartialEq)]
94pub struct ComponentEventRoute {
95    /// Name of the component this was routed through
96    pub component: String,
97    /// Downscoping that was applied on behalf of the component
98    /// None means that no downscoping was applied.
99    /// Each String is the name of a child component to which the downscope
100    /// applies.
101    pub scope: Option<Vec<String>>,
102}
103
104#[derive(Debug)]
105pub enum ServiceInstance {
106    Named(String),
107    Aggregated(usize),
108}
109
110#[derive(Debug)]
111pub enum CheckUse {
112    Protocol {
113        path: cm_types::Path,
114        expected_res: ExpectedResult,
115    },
116    Service {
117        path: cm_types::Path,
118        instance: ServiceInstance,
119        member: String,
120        expected_res: ExpectedResult,
121    },
122    Directory {
123        path: cm_types::Path,
124        file: PathBuf,
125        expected_res: ExpectedResult,
126    },
127    Storage {
128        path: cm_types::Path,
129        // The moniker from the storage declaration to the use declaration. Only
130        // used if `expected_res` is Ok.
131        storage_relation: Option<Moniker>,
132        // The backing directory for this storage is in component manager's namespace, not the
133        // test's isolated test directory.
134        from_cm_namespace: bool,
135        storage_subdir: Option<String>,
136        expected_res: ExpectedResult,
137    },
138    StorageAdmin {
139        // The moniker from the storage declaration to the use declaration.
140        storage_relation: Moniker,
141        storage_subdir: Option<String>,
142        expected_res: ExpectedResult,
143    },
144    EventStream {
145        path: cm_types::Path,
146        scope: Option<ComponentEventRoute>,
147        name: Name,
148        expected_res: ExpectedResult,
149    },
150}
151
152impl CheckUse {
153    pub fn default_directory(expected_res: ExpectedResult) -> Self {
154        Self::Directory {
155            path: default_directory_capability(),
156            file: PathBuf::from("hippo"),
157            expected_res,
158        }
159    }
160}
161
162// This function should reproduce the logic of `crate::storage::generate_storage_path`.
163pub fn generate_storage_path(
164    subdir: Option<String>,
165    moniker: &Moniker,
166    instance_id: Option<&InstanceId>,
167) -> PathBuf {
168    let mut dir_path = vec![];
169    if let Some(subdir) = subdir {
170        dir_path.push(subdir);
171    }
172    if let Some(id) = instance_id {
173        dir_path.push(id.to_string());
174        return dir_path.into_iter().collect();
175    }
176    let path = moniker.path();
177    let mut path = path.iter();
178    if let Some(p) = path.next() {
179        dir_path.push(format!("{p}:0"));
180    }
181    while let Some(p) = path.next() {
182        dir_path.push("children".to_string());
183        dir_path.push(format!("{p}:0"));
184    }
185
186    // Storage capabilities used to have a hardcoded set of types, which would be appended
187    // here. To maintain compatibility with the old paths (and thus not lose data when this was
188    // migrated) we append "data" here. This works because this is the only type of storage
189    // that was actually used in the wild.
190    dir_path.push("data".to_string());
191    dir_path.into_iter().collect()
192}
193
194/// A `RoutingTestModel` attempts to use capabilities from instances in a component model
195/// and checks the result of the attempt against an expectation.
196#[async_trait]
197pub trait RoutingTestModel {
198    type C: ComponentInstanceInterface + std::fmt::Debug + 'static;
199
200    /// Checks a `use` declaration at `moniker` by trying to use `capability`.
201    async fn check_use(&self, moniker: Moniker, check: CheckUse);
202
203    /// Checks using a capability from a component's exposed directory.
204    async fn check_use_exposed_dir(&self, moniker: Moniker, check: CheckUse);
205
206    /// Checks if the capability name referred to in the first element of the path in the
207    /// `CheckUse` can successfully be routed from the capabilities exposed to framework. Panics if
208    /// `path.split()` is longer than one element. Yes it's hacky to use the path to carry a name
209    /// here, but since this is such a small edge case it doesn't seem worth the refactor.
210    async fn check_exposed_to_framework(&self, moniker: Moniker, check: CheckUse);
211
212    /// Looks up a component instance by its moniker.
213    async fn look_up_instance(&self, moniker: &Moniker) -> Result<Arc<Self::C>, anyhow::Error>;
214
215    /// Checks that a use declaration of `path` at `moniker` can be opened with
216    /// Fuchsia file operations.
217    async fn check_open_node(&self, moniker: Moniker, path: cm_types::Path);
218
219    /// Create a file with the given contents in the test dir, along with any subdirectories
220    /// required.
221    async fn create_static_file(&self, path: &Path, contents: &str) -> Result<(), anyhow::Error>;
222
223    /// Installs a new directory at `path` in the test's namespace.
224    fn install_namespace_directory(&self, path: &str);
225
226    /// Creates a subdirectory in the outgoing dir's /data directory.
227    fn add_subdir_to_data_directory(&self, subdir: &str);
228
229    /// Asserts that the subdir given by `path` within the test directory contains exactly the
230    /// filenames in `expected`.
231    async fn check_test_subdir_contents(&self, path: &str, expected: Vec<String>);
232
233    /// Asserts that the directory at absolute `path` contains exactly the filenames in `expected`.
234    async fn check_namespace_subdir_contents(&self, path: &str, expected: Vec<String>);
235
236    /// Asserts that the subdir given by `path` within the test directory contains a file named `expected`.
237    async fn check_test_subdir_contains(&self, path: &str, expected: String);
238
239    /// Asserts that the tree in the test directory under `path` contains a file named `expected`.
240    async fn check_test_dir_tree_contains(&self, expected: String);
241}
242
243/// Builds an implementation of `RoutingTestModel` from a set of `ComponentDecl`s.
244#[async_trait]
245pub trait RoutingTestModelBuilder {
246    type Model: RoutingTestModel;
247
248    /// Create a new builder. Both string arguments refer to component names, not URLs,
249    /// ex: "a", not "test:///a" or "test:///a_resolved".
250    fn new(root_component: &str, components: Vec<(&'static str, ComponentDecl)>) -> Self;
251
252    /// Set the capabilities that should be available from the top instance's namespace.
253    fn set_namespace_capabilities(&mut self, caps: Vec<CapabilityDecl>);
254
255    /// Set the capabilities that should be available as built-in capabilities.
256    fn set_builtin_capabilities(&mut self, caps: Vec<CapabilityDecl>);
257
258    /// Register a mock `runner` in the built-in environment.
259    fn register_mock_builtin_runner(&mut self, runner: &str);
260
261    /// Add a custom capability security policy to restrict routing of certain caps.
262    fn add_capability_policy(
263        &mut self,
264        key: CapabilityAllowlistKey,
265        allowlist: HashSet<AllowlistEntry>,
266    );
267
268    /// Add a custom debug capability security policy to restrict routing of certain caps.
269    fn add_debug_capability_policy(
270        &mut self,
271        key: DebugCapabilityKey,
272        allowlist: HashSet<DebugCapabilityAllowlistEntry>,
273    );
274
275    /// Sets the path to the component ID index for the test model.
276    fn set_component_id_index_path(&mut self, path: Utf8PathBuf);
277
278    async fn build(self) -> Self::Model;
279}
280
281/// The CommonRoutingTests are run under multiple contexts, e.g. both on Fuchsia under
282/// component_manager and on the build host under cm_fidl_analyzer. This macro helps ensure that all
283/// tests are run in each context.
284#[macro_export]
285macro_rules! instantiate_common_routing_tests {
286    ($builder_impl:path) => {
287        // New CommonRoutingTest tests must be added to this list to run.
288        instantiate_common_routing_tests! {
289            $builder_impl,
290            test_use_from_parent,
291            test_use_dictionary_from_parent,
292            test_use_dictionary_allowed_by_capability_policy,
293            test_use_from_child,
294            test_use_from_self,
295            test_use_from_grandchild,
296            test_use_from_grandparent,
297            test_use_from_sibling_no_root,
298            test_use_from_sibling_root,
299            test_use_from_niece,
300            test_use_kitchen_sink,
301            test_use_from_component_manager_namespace,
302            test_offer_from_component_manager_namespace,
303            test_use_not_offered,
304            test_use_offer_source_not_exposed,
305            test_use_offer_source_not_offered,
306            test_use_from_expose,
307            test_route_protocol_from_expose,
308            test_use_from_expose_to_framework,
309            test_offer_from_non_executable,
310            test_route_filtered_aggregate_service,
311            test_route_anonymized_aggregate_service,
312            test_use_directory_with_subdir_from_grandparent,
313            test_use_directory_with_subdir_from_sibling,
314            test_expose_directory_with_subdir,
315            test_expose_from_self_and_child,
316            test_use_not_exposed,
317            test_expose_to_framework_from_self,
318            test_expose_to_framework_from_child,
319            test_use_protocol_denied_by_capability_policy,
320            test_use_directory_with_alias_denied_by_capability_policy,
321            test_use_protocol_partial_chain_allowed_by_capability_policy,
322            test_use_protocol_component_provided_capability_policy,
323            test_use_from_component_manager_namespace_denied_by_policy,
324            test_event_stream_aliasing,
325            test_use_event_stream_from_above_root,
326            test_use_event_stream_from_above_root_and_downscoped,
327            test_can_offer_capability_requested_event,
328            test_route_service_from_parent,
329            test_route_service_from_child,
330            test_route_service_from_sibling,
331            test_route_filtered_service_from_sibling,
332            test_route_renamed_service_instance_from_sibling,
333            test_use_builtin_from_grandparent,
334            test_invalid_use_from_component_manager,
335            test_invalid_offer_from_component_manager,
336            test_route_runner_from_parent_environment,
337            test_route_runner_from_grandparent_environment,
338            test_route_runner_from_sibling_environment,
339            test_route_runner_from_inherited_environment,
340            test_route_runner_from_environment_not_found,
341            test_route_builtin_runner,
342            test_route_builtin_runner_from_root_env,
343            test_route_builtin_runner_not_found,
344            test_route_builtin_runner_from_root_env_registration_not_found,
345            test_use_runner_from_child,
346            test_use_runner_from_parent,
347            test_use_runner_from_parent_environment,
348            test_use_config_from_self,
349            test_use_config_from_parent,
350            test_use_config_from_void,
351            test_use_dictionary_protocol_from_self,
352            test_offer_dictionary_to_grandchild_not_supported,
353            test_expose_dictionary_to_grandparent_not_supported,
354        }
355    };
356    ($builder_impl:path, $test:ident, $($remaining:ident),+ $(,)?) => {
357        instantiate_common_routing_tests! { $builder_impl, $test }
358        instantiate_common_routing_tests! { $builder_impl, $($remaining),+ }
359    };
360    ($builder_impl:path, $test:ident) => {
361        #[fuchsia::test]
362        async fn $test() {
363            $crate::CommonRoutingTest::<$builder_impl>::new().$test().await
364        }
365    };
366}
367
368pub struct CommonRoutingTest<T: RoutingTestModelBuilder> {
369    builder: PhantomData<T>,
370}
371impl<T: RoutingTestModelBuilder> CommonRoutingTest<T> {
372    pub fn new() -> Self {
373        Self { builder: PhantomData }
374    }
375
376    ///   a
377    ///    \
378    ///     b
379    ///
380    /// a: offers directory /data/foo from self as /data/bar
381    /// a: offers service /svc/foo from self as /svc/bar
382    /// a: offers service /svc/file from self as /svc/device
383    /// b: uses directory /data/bar as /data/hippo
384    /// b: uses service /svc/bar as /svc/hippo
385    /// b: uses service /svc/device
386    ///
387    /// The test related to `/svc/file` is used to verify that services that require
388    /// extended flags, like `OPEN_FLAG_DESCRIBE`, work correctly. This often
389    /// happens for fuchsia.hardware protocols that compose fuchsia.io protocols,
390    /// and expect that `fdio_open` should operate correctly.
391    pub async fn test_use_from_parent(&self) {
392        let components = vec![
393            (
394                "a",
395                ComponentDeclBuilder::new()
396                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
397                    .protocol_default("foo")
398                    .protocol_default("file")
399                    .offer(
400                        OfferBuilder::directory()
401                            .name("foo_data")
402                            .target_name("bar_data")
403                            .source(OfferSource::Self_)
404                            .target_static_child("b")
405                            .rights(fio::R_STAR_DIR),
406                    )
407                    .offer(
408                        OfferBuilder::protocol()
409                            .name("foo")
410                            .target_name("bar")
411                            .source(OfferSource::Self_)
412                            .target_static_child("b"),
413                    )
414                    .offer(
415                        OfferBuilder::protocol()
416                            .name("file")
417                            .target_name("device")
418                            .source(OfferSource::Self_)
419                            .target_static_child("b"),
420                    )
421                    .child_default("b")
422                    .build(),
423            ),
424            (
425                "b",
426                ComponentDeclBuilder::new()
427                    .use_(UseBuilder::directory().name("bar_data").path("/data/hippo"))
428                    .use_(UseBuilder::protocol().name("bar").path("/svc/hippo"))
429                    .use_(UseBuilder::protocol().name("device"))
430                    .build(),
431            ),
432        ];
433        let model = T::new("a", components).build().await;
434        model
435            .check_use(["b"].try_into().unwrap(), CheckUse::default_directory(ExpectedResult::Ok))
436            .await;
437        model
438            .check_use(
439                ["b"].try_into().unwrap(),
440                CheckUse::Protocol {
441                    path: default_service_capability(),
442                    expected_res: ExpectedResult::Ok,
443                },
444            )
445            .await;
446        model.check_open_node(["b"].try_into().unwrap(), "/svc/device".parse().unwrap()).await;
447    }
448
449    ///   a
450    ///    \
451    ///     b
452    ///
453    /// a: declares dictionary my_dictionary
454    /// a: offers directory /data/foo from self as /data/bar to my_dictionary
455    /// a: offers protocol /svc/foo from self as /svc/bar to my_dictionary
456    /// a: offers dictionary my_dictionary to b
457    /// b: uses dictionary my_dictionary at path /dictionary
458    pub async fn test_use_dictionary_from_parent(&self) {
459        let components = vec![
460            (
461                "a",
462                ComponentDeclBuilder::new()
463                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
464                    .protocol_default("foo")
465                    .dictionary_default("my_dictionary")
466                    .offer(
467                        OfferBuilder::directory()
468                            .name("foo_data")
469                            .target_name("bar_data")
470                            .source(OfferSource::Self_)
471                            .target_capability("my_dictionary")
472                            .rights(fio::R_STAR_DIR),
473                    )
474                    .offer(
475                        OfferBuilder::protocol()
476                            .name("foo")
477                            .target_name("bar")
478                            .source(OfferSource::Self_)
479                            .target_capability("my_dictionary"),
480                    )
481                    .offer(
482                        OfferBuilder::dictionary()
483                            .name("my_dictionary")
484                            .source(OfferSource::Self_)
485                            .target_static_child("b"),
486                    )
487                    .child_default("b")
488                    .build(),
489            ),
490            (
491                "b",
492                ComponentDeclBuilder::new()
493                    .use_(UseBuilder::dictionary().name("my_dictionary").path("/my_dictionary"))
494                    .build(),
495            ),
496        ];
497        let model = T::new("a", components).build().await;
498        model
499            .create_static_file(Path::new("foo/example_file"), "hello")
500            .await
501            .expect("failed to create file");
502        model
503            .check_use(
504                ["b"].try_into().unwrap(),
505                CheckUse::Protocol {
506                    path: "/my_dictionary/bar".parse().unwrap(),
507                    expected_res: ExpectedResult::Ok,
508                },
509            )
510            .await;
511        model
512            .check_use(
513                ["b"].try_into().unwrap(),
514                CheckUse::Directory {
515                    path: "/my_dictionary".parse().unwrap(),
516                    file: PathBuf::from("bar_data/example_file"),
517                    expected_res: ExpectedResult::Ok,
518                },
519            )
520            .await;
521    }
522
523    ///   a
524    ///    \
525    ///     b
526    /// a: offers protocols to dictionary and offers dictionary to b.
527    /// b: uses protocol from dictionary, with policy allowing it.
528    pub async fn test_use_dictionary_allowed_by_capability_policy(&self) {
529        const DICT_NAME: &str = "dict";
530        const PROTO_NAME: &str = "hippo";
531        let components = vec![
532            (
533                "a",
534                ComponentDeclBuilder::new()
535                    .dictionary_default(DICT_NAME)
536                    .protocol_default(PROTO_NAME)
537                    .offer(
538                        OfferBuilder::protocol()
539                            .name(PROTO_NAME)
540                            .source(OfferSource::Self_)
541                            .target_capability(DICT_NAME),
542                    )
543                    .offer(
544                        OfferBuilder::dictionary()
545                            .name(DICT_NAME)
546                            .source(OfferSource::Self_)
547                            .target_static_child("b"),
548                    )
549                    .child_default("b")
550                    .build(),
551            ),
552            (
553                "b",
554                ComponentDeclBuilder::new()
555                    .use_(UseBuilder::dictionary().name(DICT_NAME).path("/svc"))
556                    .build(),
557            ),
558        ];
559
560        let mut allowlist = HashSet::new();
561        allowlist.insert(AllowlistEntryBuilder::new().exact("b").build());
562
563        let mut builder = T::new("a", components);
564        builder.add_capability_policy(
565            CapabilityAllowlistKey {
566                source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
567                source_name: PROTO_NAME.parse().unwrap(),
568                source: CapabilityAllowlistSource::Self_,
569                capability: CapabilityTypeName::Protocol,
570            },
571            allowlist,
572        );
573        let model = builder.build().await;
574
575        model
576            .check_use(
577                ["b"].try_into().unwrap(),
578                CheckUse::Protocol {
579                    path: format!("/svc/{PROTO_NAME}").parse().unwrap(),
580                    expected_res: ExpectedResult::Ok,
581                },
582            )
583            .await;
584    }
585
586    ///   a
587    ///    \
588    ///     b
589    ///
590    /// a: uses directory /data/bar from #b as /data/hippo
591    /// a: uses service /svc/bar from #b as /svc/hippo
592    /// b: exposes directory /data/foo from self as /data/bar
593    /// b: exposes service /svc/foo from self as /svc/bar
594    pub async fn test_use_from_child(&self) {
595        let components = vec![
596            (
597                "a",
598                ComponentDeclBuilder::new()
599                    .use_(
600                        UseBuilder::directory()
601                            .source_static_child("b")
602                            .name("bar_data")
603                            .path("/data/hippo"),
604                    )
605                    .use_(
606                        UseBuilder::protocol()
607                            .source_static_child("b")
608                            .name("bar")
609                            .path("/svc/hippo"),
610                    )
611                    .child_default("b")
612                    .build(),
613            ),
614            (
615                "b",
616                ComponentDeclBuilder::new()
617                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
618                    .protocol_default("foo")
619                    .expose(
620                        ExposeBuilder::directory()
621                            .name("foo_data")
622                            .source(ExposeSource::Self_)
623                            .target_name("bar_data")
624                            .rights(fio::R_STAR_DIR),
625                    )
626                    .expose(
627                        ExposeBuilder::protocol()
628                            .name("foo")
629                            .target_name("bar")
630                            .source(ExposeSource::Self_),
631                    )
632                    .build(),
633            ),
634        ];
635        let model = T::new("a", components).build().await;
636        model.check_use(Moniker::root(), CheckUse::default_directory(ExpectedResult::Ok)).await;
637        model
638            .check_use(
639                Moniker::root(),
640                CheckUse::Protocol {
641                    path: default_service_capability(),
642                    expected_res: ExpectedResult::Ok,
643                },
644            )
645            .await;
646    }
647
648    /// a: uses protocol /svc/hippo from self
649    pub async fn test_use_from_self(&self) {
650        let components = vec![(
651            "a",
652            ComponentDeclBuilder::new()
653                .protocol_default("hippo")
654                .use_(UseBuilder::protocol().source(UseSource::Self_).name("hippo"))
655                .build(),
656        )];
657        let model = T::new("a", components).build().await;
658        model
659            .check_use(
660                Moniker::root(),
661                CheckUse::Protocol {
662                    path: default_service_capability(),
663                    expected_res: ExpectedResult::Ok,
664                },
665            )
666            .await;
667    }
668
669    ///   a
670    ///    \
671    ///     b
672    ///      \
673    ///       c
674    ///
675    /// a: uses /data/baz from #b as /data/hippo
676    /// a: uses /svc/baz from #b as /svc/hippo
677    /// b: exposes directory /data/bar from #c as /data/baz
678    /// b: exposes service /svc/bar from #c as /svc/baz
679    /// c: exposes directory /data/foo from self as /data/bar
680    /// c: exposes service /svc/foo from self as /svc/bar
681    pub async fn test_use_from_grandchild(&self) {
682        let components = vec![
683            (
684                "a",
685                ComponentDeclBuilder::new()
686                    .use_(
687                        UseBuilder::directory()
688                            .source_static_child("b")
689                            .name("baz_data")
690                            .path("/data/hippo"),
691                    )
692                    .use_(
693                        UseBuilder::protocol()
694                            .source_static_child("b")
695                            .name("baz")
696                            .path("/svc/hippo"),
697                    )
698                    .child_default("b")
699                    .build(),
700            ),
701            (
702                "b",
703                ComponentDeclBuilder::new()
704                    .expose(
705                        ExposeBuilder::directory()
706                            .name("bar_data")
707                            .source_static_child("c")
708                            .target_name("baz_data")
709                            .rights(fio::R_STAR_DIR),
710                    )
711                    .expose(
712                        ExposeBuilder::protocol()
713                            .name("bar")
714                            .target_name("baz")
715                            .source_static_child("c"),
716                    )
717                    .child_default("c")
718                    .build(),
719            ),
720            (
721                "c",
722                ComponentDeclBuilder::new()
723                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
724                    .protocol_default("foo")
725                    .expose(
726                        ExposeBuilder::directory()
727                            .name("foo_data")
728                            .source(ExposeSource::Self_)
729                            .target_name("bar_data")
730                            .rights(fio::R_STAR_DIR),
731                    )
732                    .expose(
733                        ExposeBuilder::protocol()
734                            .name("foo")
735                            .target_name("bar")
736                            .source(ExposeSource::Self_),
737                    )
738                    .build(),
739            ),
740        ];
741        let model = T::new("a", components).build().await;
742        model.check_use(Moniker::root(), CheckUse::default_directory(ExpectedResult::Ok)).await;
743        model
744            .check_use(
745                Moniker::root(),
746                CheckUse::Protocol {
747                    path: default_service_capability(),
748                    expected_res: ExpectedResult::Ok,
749                },
750            )
751            .await;
752    }
753
754    ///   a
755    ///    \
756    ///     b
757    ///      \
758    ///       c
759    ///
760    /// a: offers directory /data/foo from self as /data/bar
761    /// a: offers service /svc/foo from self as /svc/bar
762    /// b: offers directory /data/bar from realm as /data/baz
763    /// b: offers service /svc/bar from realm as /svc/baz
764    /// c: uses /data/baz as /data/hippo
765    /// c: uses /svc/baz as /svc/hippo
766    pub async fn test_use_from_grandparent(&self) {
767        let components = vec![
768            (
769                "a",
770                ComponentDeclBuilder::new()
771                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
772                    .protocol_default("foo")
773                    .offer(
774                        OfferBuilder::directory()
775                            .name("foo_data")
776                            .target_name("bar_data")
777                            .source(OfferSource::Self_)
778                            .target_static_child("b")
779                            .rights(fio::R_STAR_DIR),
780                    )
781                    .offer(
782                        OfferBuilder::protocol()
783                            .name("foo")
784                            .target_name("bar")
785                            .source(OfferSource::Self_)
786                            .target_static_child("b"),
787                    )
788                    .child_default("b")
789                    .build(),
790            ),
791            (
792                "b",
793                ComponentDeclBuilder::new()
794                    .offer(
795                        OfferBuilder::directory()
796                            .name("bar_data")
797                            .target_name("baz_data")
798                            .source(OfferSource::Parent)
799                            .target_static_child("c")
800                            .rights(fio::R_STAR_DIR),
801                    )
802                    .offer(
803                        OfferBuilder::protocol()
804                            .name("bar")
805                            .target_name("baz")
806                            .source(OfferSource::Parent)
807                            .target_static_child("c"),
808                    )
809                    .child_default("c")
810                    .build(),
811            ),
812            (
813                "c",
814                ComponentDeclBuilder::new()
815                    .use_(UseBuilder::directory().name("baz_data").path("/data/hippo"))
816                    .use_(UseBuilder::protocol().name("baz").path("/svc/hippo"))
817                    .build(),
818            ),
819        ];
820        let model = T::new("a", components).build().await;
821        model
822            .check_use(
823                ["b", "c"].try_into().unwrap(),
824                CheckUse::default_directory(ExpectedResult::Ok),
825            )
826            .await;
827        model
828            .check_use(
829                ["b", "c"].try_into().unwrap(),
830                CheckUse::Protocol {
831                    path: default_service_capability(),
832                    expected_res: ExpectedResult::Ok,
833                },
834            )
835            .await;
836    }
837
838    ///   a
839    ///    \
840    ///     b
841    ///      \
842    ///       c
843    ///
844    /// a: offers service /svc/builtin.Echo from realm
845    /// b: offers service /svc/builtin.Echo from realm
846    /// c: uses /svc/builtin.Echo as /svc/hippo
847    pub async fn test_use_builtin_from_grandparent(&self) {
848        let components = vec![
849            (
850                "a",
851                ComponentDeclBuilder::new()
852                    .offer(
853                        OfferBuilder::protocol()
854                            .name("builtin.Echo")
855                            .source(OfferSource::Parent)
856                            .target_static_child("b"),
857                    )
858                    .child_default("b")
859                    .build(),
860            ),
861            (
862                "b",
863                ComponentDeclBuilder::new()
864                    .offer(
865                        OfferBuilder::protocol()
866                            .name("builtin.Echo")
867                            .source(OfferSource::Parent)
868                            .target_static_child("c"),
869                    )
870                    .child_default("c")
871                    .build(),
872            ),
873            (
874                "c",
875                ComponentDeclBuilder::new()
876                    .use_(UseBuilder::protocol().name("builtin.Echo").path("/svc/hippo"))
877                    .build(),
878            ),
879        ];
880
881        let mut builder = T::new("a", components);
882        builder.set_builtin_capabilities(vec![CapabilityDecl::Protocol(ProtocolDecl {
883            name: "builtin.Echo".parse().unwrap(),
884            source_path: None,
885            delivery: Default::default(),
886        })]);
887        let model = builder.build().await;
888
889        model
890            .check_use(
891                ["b", "c"].try_into().unwrap(),
892                CheckUse::Protocol {
893                    path: default_service_capability(),
894                    expected_res: ExpectedResult::Ok,
895                },
896            )
897            .await;
898    }
899
900    ///     a
901    ///    /
902    ///   b
903    ///  / \
904    /// d   c
905    ///
906    /// d: exposes directory /data/foo from self as /data/bar
907    /// b: offers directory /data/bar from d as /data/foobar to c
908    /// c: uses /data/foobar as /data/hippo
909    pub async fn test_use_from_sibling_no_root(&self) {
910        let components = vec![
911            ("a", ComponentDeclBuilder::new().child_default("b").build()),
912            (
913                "b",
914                ComponentDeclBuilder::new()
915                    .offer(
916                        OfferBuilder::directory()
917                            .name("bar_data")
918                            .target_name("foobar_data")
919                            .source_static_child("d")
920                            .target_static_child("c")
921                            .rights(fio::R_STAR_DIR),
922                    )
923                    .offer(
924                        OfferBuilder::protocol()
925                            .name("bar")
926                            .target_name("foobar")
927                            .source_static_child("d")
928                            .target_static_child("c"),
929                    )
930                    .child_default("c")
931                    .child_default("d")
932                    .build(),
933            ),
934            (
935                "c",
936                ComponentDeclBuilder::new()
937                    .use_(UseBuilder::directory().name("foobar_data").path("/data/hippo"))
938                    .use_(UseBuilder::protocol().name("foobar").path("/svc/hippo"))
939                    .build(),
940            ),
941            (
942                "d",
943                ComponentDeclBuilder::new()
944                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
945                    .protocol_default("foo")
946                    .expose(
947                        ExposeBuilder::directory()
948                            .name("foo_data")
949                            .source(ExposeSource::Self_)
950                            .target_name("bar_data")
951                            .rights(fio::R_STAR_DIR),
952                    )
953                    .expose(
954                        ExposeBuilder::protocol()
955                            .name("foo")
956                            .target_name("bar")
957                            .source(ExposeSource::Self_),
958                    )
959                    .build(),
960            ),
961        ];
962        let model = T::new("a", components).build().await;
963        model
964            .check_use(
965                ["b", "c"].try_into().unwrap(),
966                CheckUse::default_directory(ExpectedResult::Ok),
967            )
968            .await;
969        model
970            .check_use(
971                ["b", "c"].try_into().unwrap(),
972                CheckUse::Protocol {
973                    path: default_service_capability(),
974                    expected_res: ExpectedResult::Ok,
975                },
976            )
977            .await;
978    }
979
980    ///   a
981    ///  / \
982    /// b   c
983    ///
984    /// b: exposes directory /data/foo from self as /data/bar
985    /// a: offers directory /data/bar from b as /data/baz to c
986    /// c: uses /data/baz as /data/hippo
987    pub async fn test_use_from_sibling_root(&self) {
988        let components = vec![
989            (
990                "a",
991                ComponentDeclBuilder::new()
992                    .offer(
993                        OfferBuilder::directory()
994                            .name("bar_data")
995                            .target_name("baz_data")
996                            .source_static_child("b")
997                            .target_static_child("c")
998                            .rights(fio::R_STAR_DIR),
999                    )
1000                    .offer(
1001                        OfferBuilder::protocol()
1002                            .name("bar")
1003                            .target_name("baz")
1004                            .source_static_child("b")
1005                            .target_static_child("c"),
1006                    )
1007                    .child_default("b")
1008                    .child_default("c")
1009                    .build(),
1010            ),
1011            (
1012                "b",
1013                ComponentDeclBuilder::new()
1014                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
1015                    .protocol_default("foo")
1016                    .expose(
1017                        ExposeBuilder::directory()
1018                            .name("foo_data")
1019                            .source(ExposeSource::Self_)
1020                            .target_name("bar_data")
1021                            .rights(fio::R_STAR_DIR),
1022                    )
1023                    .expose(
1024                        ExposeBuilder::protocol()
1025                            .name("foo")
1026                            .target_name("bar")
1027                            .source(ExposeSource::Self_),
1028                    )
1029                    .build(),
1030            ),
1031            (
1032                "c",
1033                ComponentDeclBuilder::new()
1034                    .use_(UseBuilder::directory().name("baz_data").path("/data/hippo"))
1035                    .use_(UseBuilder::protocol().name("baz").path("/svc/hippo"))
1036                    .build(),
1037            ),
1038        ];
1039        let model = T::new("a", components).build().await;
1040        model
1041            .check_use(["c"].try_into().unwrap(), CheckUse::default_directory(ExpectedResult::Ok))
1042            .await;
1043        model
1044            .check_use(
1045                ["c"].try_into().unwrap(),
1046                CheckUse::Protocol {
1047                    path: default_service_capability(),
1048                    expected_res: ExpectedResult::Ok,
1049                },
1050            )
1051            .await;
1052    }
1053
1054    ///     a
1055    ///    / \
1056    ///   b   c
1057    ///  /
1058    /// d
1059    ///
1060    /// d: exposes directory /data/foo from self as /data/bar
1061    /// b: exposes directory /data/bar from d as /data/baz
1062    /// a: offers directory /data/baz from b as /data/foobar to c
1063    /// c: uses /data/foobar as /data/hippo
1064    pub async fn test_use_from_niece(&self) {
1065        let components = vec![
1066            (
1067                "a",
1068                ComponentDeclBuilder::new()
1069                    .offer(
1070                        OfferBuilder::directory()
1071                            .name("baz_data")
1072                            .target_name("foobar_data")
1073                            .source_static_child("b")
1074                            .target_static_child("c")
1075                            .rights(fio::R_STAR_DIR),
1076                    )
1077                    .offer(
1078                        OfferBuilder::protocol()
1079                            .name("baz")
1080                            .target_name("foobar")
1081                            .source_static_child("b")
1082                            .target_static_child("c"),
1083                    )
1084                    .child_default("b")
1085                    .child_default("c")
1086                    .build(),
1087            ),
1088            (
1089                "b",
1090                ComponentDeclBuilder::new()
1091                    .expose(
1092                        ExposeBuilder::directory()
1093                            .name("bar_data")
1094                            .source_static_child("d")
1095                            .target_name("baz_data")
1096                            .rights(fio::R_STAR_DIR),
1097                    )
1098                    .expose(
1099                        ExposeBuilder::protocol()
1100                            .name("bar")
1101                            .target_name("baz")
1102                            .source_static_child("d"),
1103                    )
1104                    .child_default("d")
1105                    .build(),
1106            ),
1107            (
1108                "c",
1109                ComponentDeclBuilder::new()
1110                    .use_(UseBuilder::directory().name("foobar_data").path("/data/hippo"))
1111                    .use_(UseBuilder::protocol().name("foobar").path("/svc/hippo"))
1112                    .build(),
1113            ),
1114            (
1115                "d",
1116                ComponentDeclBuilder::new()
1117                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
1118                    .protocol_default("foo")
1119                    .expose(
1120                        ExposeBuilder::directory()
1121                            .name("foo_data")
1122                            .source(ExposeSource::Self_)
1123                            .target_name("bar_data")
1124                            .rights(fio::R_STAR_DIR),
1125                    )
1126                    .expose(
1127                        ExposeBuilder::protocol()
1128                            .name("foo")
1129                            .target_name("bar")
1130                            .source(ExposeSource::Self_),
1131                    )
1132                    .build(),
1133            ),
1134        ];
1135        let model = T::new("a", components).build().await;
1136        model
1137            .check_use(["c"].try_into().unwrap(), CheckUse::default_directory(ExpectedResult::Ok))
1138            .await;
1139        model
1140            .check_use(
1141                ["c"].try_into().unwrap(),
1142                CheckUse::Protocol {
1143                    path: default_service_capability(),
1144                    expected_res: ExpectedResult::Ok,
1145                },
1146            )
1147            .await;
1148    }
1149
1150    ///      a
1151    ///     / \
1152    ///    /   \
1153    ///   b     c
1154    ///  / \   / \
1155    /// d   e f   g
1156    ///            \
1157    ///             h
1158    ///
1159    /// a,d,h: hosts /svc/foo and /data/foo
1160    /// e: uses /svc/foo as /svc/hippo from a, uses /data/foo as /data/hippo from d
1161    /// f: uses /data/foo from d as /data/hippo, uses /svc/foo from h as /svc/hippo
1162    pub async fn test_use_kitchen_sink(&self) {
1163        let components = vec![
1164            (
1165                "a",
1166                ComponentDeclBuilder::new()
1167                    .protocol_default("foo")
1168                    .offer(
1169                        OfferBuilder::protocol()
1170                            .name("foo")
1171                            .target_name("foo_from_a_svc")
1172                            .source(OfferSource::Self_)
1173                            .target_static_child("b"),
1174                    )
1175                    .offer(
1176                        OfferBuilder::directory()
1177                            .name("foo_from_d_data")
1178                            .source_static_child("b")
1179                            .target_static_child("c")
1180                            .rights(fio::R_STAR_DIR),
1181                    )
1182                    .child_default("b")
1183                    .child_default("c")
1184                    .build(),
1185            ),
1186            (
1187                "b",
1188                ComponentDeclBuilder::new_empty_component()
1189                    .offer(
1190                        OfferBuilder::directory()
1191                            .name("foo_from_d_data")
1192                            .source_static_child("d")
1193                            .target_static_child("e")
1194                            .rights(fio::R_STAR_DIR),
1195                    )
1196                    .offer(
1197                        OfferBuilder::protocol()
1198                            .name("foo_from_a_svc")
1199                            .source(OfferSource::Parent)
1200                            .target_static_child("e"),
1201                    )
1202                    .expose(
1203                        ExposeBuilder::directory()
1204                            .name("foo_from_d_data")
1205                            .source_static_child("d")
1206                            .rights(fio::R_STAR_DIR),
1207                    )
1208                    .child_default("d")
1209                    .child_default("e")
1210                    .build(),
1211            ),
1212            (
1213                "c",
1214                ComponentDeclBuilder::new_empty_component()
1215                    .offer(
1216                        OfferBuilder::directory()
1217                            .name("foo_from_d_data")
1218                            .source(OfferSource::Parent)
1219                            .target_static_child("f")
1220                            .rights(fio::R_STAR_DIR),
1221                    )
1222                    .offer(
1223                        OfferBuilder::protocol()
1224                            .name("foo_from_h_svc")
1225                            .source_static_child("g")
1226                            .target_static_child("f"),
1227                    )
1228                    .child_default("f")
1229                    .child_default("g")
1230                    .build(),
1231            ),
1232            (
1233                "d",
1234                ComponentDeclBuilder::new()
1235                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
1236                    .expose(
1237                        ExposeBuilder::directory()
1238                            .name("foo_data")
1239                            .source(ExposeSource::Self_)
1240                            .target_name("foo_from_d_data")
1241                            .rights(fio::R_STAR_DIR),
1242                    )
1243                    .build(),
1244            ),
1245            (
1246                "e",
1247                ComponentDeclBuilder::new()
1248                    .use_(UseBuilder::directory().name("foo_from_d_data").path("/data/hippo"))
1249                    .use_(UseBuilder::protocol().name("foo_from_a_svc").path("/svc/hippo"))
1250                    .build(),
1251            ),
1252            (
1253                "f",
1254                ComponentDeclBuilder::new()
1255                    .use_(UseBuilder::directory().name("foo_from_d_data").path("/data/hippo"))
1256                    .use_(UseBuilder::protocol().name("foo_from_h_svc").path("/svc/hippo"))
1257                    .build(),
1258            ),
1259            (
1260                "g",
1261                ComponentDeclBuilder::new_empty_component()
1262                    .expose(
1263                        ExposeBuilder::protocol().name("foo_from_h_svc").source_static_child("h"),
1264                    )
1265                    .child_default("h")
1266                    .build(),
1267            ),
1268            (
1269                "h",
1270                ComponentDeclBuilder::new()
1271                    .protocol_default("foo")
1272                    .expose(
1273                        ExposeBuilder::protocol()
1274                            .name("foo")
1275                            .target_name("foo_from_h_svc")
1276                            .source(ExposeSource::Self_),
1277                    )
1278                    .build(),
1279            ),
1280        ];
1281        let model = T::new("a", components).build().await;
1282        model
1283            .check_use(
1284                ["b", "e"].try_into().unwrap(),
1285                CheckUse::default_directory(ExpectedResult::Ok),
1286            )
1287            .await;
1288        model
1289            .check_use(
1290                ["b", "e"].try_into().unwrap(),
1291                CheckUse::Protocol {
1292                    path: default_service_capability(),
1293                    expected_res: ExpectedResult::Ok,
1294                },
1295            )
1296            .await;
1297        model
1298            .check_use(
1299                ["c", "f"].try_into().unwrap(),
1300                CheckUse::default_directory(ExpectedResult::Ok),
1301            )
1302            .await;
1303        model
1304            .check_use(
1305                ["c", "f"].try_into().unwrap(),
1306                CheckUse::Protocol {
1307                    path: default_service_capability(),
1308                    expected_res: ExpectedResult::Ok,
1309                },
1310            )
1311            .await;
1312    }
1313
1314    ///  component manager's namespace
1315    ///   |
1316    ///   a
1317    ///
1318    /// a: uses directory /use_from_cm_namespace/data/foo as foo_data
1319    /// a: uses service /use_from_cm_namespace/svc/foo as foo
1320    pub async fn test_use_from_component_manager_namespace(&self) {
1321        let components = vec![(
1322            "a",
1323            ComponentDeclBuilder::new()
1324                .use_(UseBuilder::directory().name("foo_data").path("/data/hippo"))
1325                .use_(UseBuilder::protocol().name("foo").path("/svc/hippo"))
1326                .build(),
1327        )];
1328        let namespace_capabilities = vec![
1329            CapabilityBuilder::directory()
1330                .name("foo_data")
1331                .path("/use_from_cm_namespace/data/foo")
1332                .build(),
1333            CapabilityBuilder::protocol()
1334                .name("foo")
1335                .path("/use_from_cm_namespace/svc/foo")
1336                .build(),
1337        ];
1338        let mut builder = T::new("a", components);
1339        builder.set_namespace_capabilities(namespace_capabilities);
1340        let model = builder.build().await;
1341
1342        model.install_namespace_directory("/use_from_cm_namespace");
1343        model.check_use(Moniker::root(), CheckUse::default_directory(ExpectedResult::Ok)).await;
1344        model
1345            .check_use(
1346                Moniker::root(),
1347                CheckUse::Protocol {
1348                    path: default_service_capability(),
1349                    expected_res: ExpectedResult::Ok,
1350                },
1351            )
1352            .await;
1353    }
1354
1355    ///  component manager's namespace
1356    ///   |
1357    ///   a
1358    ///    \
1359    ///     b
1360    ///
1361    /// a: offers directory /offer_from_cm_namespace/data/foo from realm as bar_data
1362    /// a: offers service /offer_from_cm_namespace/svc/foo from realm as bar
1363    /// b: uses directory bar_data as /data/hippo
1364    /// b: uses service bar as /svc/hippo
1365    pub async fn test_offer_from_component_manager_namespace(&self) {
1366        let components = vec![
1367            (
1368                "a",
1369                ComponentDeclBuilder::new()
1370                    .offer(
1371                        OfferBuilder::directory()
1372                            .name("foo_data")
1373                            .target_name("bar_data")
1374                            .source(OfferSource::Parent)
1375                            .target_static_child("b")
1376                            .rights(fio::R_STAR_DIR),
1377                    )
1378                    .offer(
1379                        OfferBuilder::protocol()
1380                            .name("foo")
1381                            .target_name("bar")
1382                            .source(OfferSource::Parent)
1383                            .target_static_child("b"),
1384                    )
1385                    .child_default("b")
1386                    .build(),
1387            ),
1388            (
1389                "b",
1390                ComponentDeclBuilder::new()
1391                    .use_(UseBuilder::directory().name("bar_data").path("/data/hippo"))
1392                    .use_(UseBuilder::protocol().name("bar").path("/svc/hippo"))
1393                    .build(),
1394            ),
1395        ];
1396        let namespace_capabilities = vec![
1397            CapabilityBuilder::directory()
1398                .name("foo_data")
1399                .path("/offer_from_cm_namespace/data/foo")
1400                .build(),
1401            CapabilityBuilder::protocol()
1402                .name("foo")
1403                .path("/offer_from_cm_namespace/svc/foo")
1404                .build(),
1405        ];
1406        let mut builder = T::new("a", components);
1407        builder.set_namespace_capabilities(namespace_capabilities);
1408        let model = builder.build().await;
1409
1410        model.install_namespace_directory("/offer_from_cm_namespace");
1411        model
1412            .check_use(["b"].try_into().unwrap(), CheckUse::default_directory(ExpectedResult::Ok))
1413            .await;
1414        model
1415            .check_use(
1416                ["b"].try_into().unwrap(),
1417                CheckUse::Protocol {
1418                    path: default_service_capability(),
1419                    expected_res: ExpectedResult::Ok,
1420                },
1421            )
1422            .await;
1423    }
1424
1425    ///   a
1426    ///    \
1427    ///     b
1428    ///
1429    /// b: uses directory /data/hippo as /data/hippo, but it's not in its realm
1430    /// b: uses service /svc/hippo as /svc/hippo, but it's not in its realm
1431    pub async fn test_use_not_offered(&self) {
1432        let components = vec![
1433            ("a", ComponentDeclBuilder::new_empty_component().child_default("b").build()),
1434            (
1435                "b",
1436                ComponentDeclBuilder::new()
1437                    .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1438                    .use_(UseBuilder::protocol().name("hippo"))
1439                    .build(),
1440            ),
1441        ];
1442        let model = T::new("a", components).build().await;
1443        model
1444            .check_use(
1445                ["b"].try_into().unwrap(),
1446                CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1447            )
1448            .await;
1449        model
1450            .check_use(
1451                ["b"].try_into().unwrap(),
1452                CheckUse::Protocol {
1453                    path: default_service_capability(),
1454                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1455                },
1456            )
1457            .await;
1458    }
1459
1460    ///   a
1461    ///  / \
1462    /// b   c
1463    ///
1464    /// a: offers directory /data/hippo from b as /data/hippo, but it's not exposed by b
1465    /// a: offers service /svc/hippo from b as /svc/hippo, but it's not exposed by b
1466    /// c: uses directory /data/hippo as /data/hippo
1467    /// c: uses service /svc/hippo as /svc/hippo
1468    pub async fn test_use_offer_source_not_exposed(&self) {
1469        let components = vec![
1470            (
1471                "a",
1472                ComponentDeclBuilder::new_empty_component()
1473                    .offer(
1474                        OfferBuilder::directory()
1475                            .name("hippo_data")
1476                            .source_static_child("b")
1477                            .target_static_child("c")
1478                            .rights(fio::R_STAR_DIR),
1479                    )
1480                    .offer(
1481                        OfferBuilder::protocol()
1482                            .name("hippo")
1483                            .source_static_child("b")
1484                            .target_static_child("c"),
1485                    )
1486                    .child_default("b")
1487                    .child_default("c")
1488                    .build(),
1489            ),
1490            ("b", component_decl_with_test_runner()),
1491            (
1492                "c",
1493                ComponentDeclBuilder::new()
1494                    .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1495                    .use_(UseBuilder::protocol().name("hippo"))
1496                    .build(),
1497            ),
1498        ];
1499        let model = T::new("a", components).build().await;
1500        model
1501            .check_use(
1502                ["c"].try_into().unwrap(),
1503                CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1504            )
1505            .await;
1506        model
1507            .check_use(
1508                ["c"].try_into().unwrap(),
1509                CheckUse::Protocol {
1510                    path: default_service_capability(),
1511                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1512                },
1513            )
1514            .await;
1515    }
1516
1517    ///   a
1518    ///    \
1519    ///     b
1520    ///      \
1521    ///       c
1522    ///
1523    /// b: offers directory /data/hippo from its realm as /data/hippo, but it's not offered by a
1524    /// b: offers service /svc/hippo from its realm as /svc/hippo, but it's not offfered by a
1525    /// c: uses directory /data/hippo as /data/hippo
1526    /// c: uses service /svc/hippo as /svc/hippo
1527    pub async fn test_use_offer_source_not_offered(&self) {
1528        let components = vec![
1529            ("a", ComponentDeclBuilder::new().child_default("b").build()),
1530            (
1531                "b",
1532                ComponentDeclBuilder::new_empty_component()
1533                    .offer(
1534                        OfferBuilder::directory()
1535                            .name("hippo_data")
1536                            .source(OfferSource::Parent)
1537                            .target_static_child("c")
1538                            .rights(fio::R_STAR_DIR),
1539                    )
1540                    .offer(
1541                        OfferBuilder::protocol()
1542                            .name("hippo")
1543                            .source(OfferSource::Parent)
1544                            .target_static_child("c"),
1545                    )
1546                    .child_default("c")
1547                    .build(),
1548            ),
1549            (
1550                "c",
1551                ComponentDeclBuilder::new()
1552                    .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1553                    .use_(UseBuilder::protocol().name("hippo"))
1554                    .build(),
1555            ),
1556        ];
1557        let test = T::new("a", components).build().await;
1558        test.check_use(
1559            ["b", "c"].try_into().unwrap(),
1560            CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1561        )
1562        .await;
1563        test.check_use(
1564            ["b", "c"].try_into().unwrap(),
1565            CheckUse::Protocol {
1566                path: default_service_capability(),
1567                expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1568            },
1569        )
1570        .await;
1571    }
1572
1573    ///   a
1574    ///    \
1575    ///     b
1576    ///      \
1577    ///       c
1578    ///
1579    /// b: uses directory /data/hippo as /data/hippo, but it's exposed to it, not offered
1580    /// b: uses service /svc/hippo as /svc/hippo, but it's exposed to it, not offered
1581    /// c: exposes /data/hippo
1582    /// c: exposes /svc/hippo
1583    pub async fn test_use_from_expose(&self) {
1584        let components = vec![
1585            ("a", ComponentDeclBuilder::new_empty_component().child_default("b").build()),
1586            (
1587                "b",
1588                ComponentDeclBuilder::new()
1589                    .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1590                    .use_(UseBuilder::protocol().name("hippo"))
1591                    .child_default("c")
1592                    .build(),
1593            ),
1594            (
1595                "c",
1596                ComponentDeclBuilder::new()
1597                    .capability(CapabilityBuilder::directory().name("hippo_data").path("/data/foo"))
1598                    .protocol_default("hippo")
1599                    .expose(
1600                        ExposeBuilder::directory()
1601                            .name("hippo_data")
1602                            .source(ExposeSource::Self_)
1603                            .rights(fio::R_STAR_DIR),
1604                    )
1605                    .expose(ExposeBuilder::protocol().name("hippo").source(ExposeSource::Self_))
1606                    .build(),
1607            ),
1608        ];
1609        let model = T::new("a", components).build().await;
1610        model
1611            .check_use(
1612                ["b"].try_into().unwrap(),
1613                CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1614            )
1615            .await;
1616        model
1617            .check_use(
1618                ["b"].try_into().unwrap(),
1619                CheckUse::Protocol {
1620                    path: default_service_capability(),
1621                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1622                },
1623            )
1624            .await;
1625    }
1626
1627    /// a
1628    ///  \
1629    ///   b
1630    ///
1631    /// a: exposes "foo" to parent from child
1632    /// b: exposes "foo" to parent from self
1633    pub async fn test_route_protocol_from_expose(&self) {
1634        let expose_decl = ExposeBuilder::protocol()
1635            .name("foo")
1636            .source(ExposeSource::Child("b".parse().unwrap()))
1637            .build();
1638        let expected_protocol_decl = CapabilityBuilder::protocol().name("foo").build();
1639
1640        let components = vec![
1641            (
1642                "a",
1643                ComponentDeclBuilder::new().expose(expose_decl.clone()).child_default("b").build(),
1644            ),
1645            (
1646                "b",
1647                ComponentDeclBuilder::new()
1648                    .expose(ExposeBuilder::protocol().name("foo").source(ExposeSource::Self_))
1649                    .capability(expected_protocol_decl.clone())
1650                    .build(),
1651            ),
1652        ];
1653        let model = T::new("a", components).build().await;
1654        let root_instance = model.look_up_instance(&Moniker::root()).await.expect("root instance");
1655        let expected_source_moniker = Moniker::parse_str("/b").unwrap();
1656
1657        let CapabilityDecl::Protocol(expected_protocol_decl) = expected_protocol_decl else {
1658            unreachable!();
1659        };
1660        assert_matches!(
1661            debug_route_sandbox_path(
1662                &root_instance,
1663                &expose_decl,
1664            ).await,
1665            Ok(CapabilitySource::Component(ComponentSource {
1666                capability: ComponentCapability::Protocol(capability_decl),
1667                moniker,
1668            })) if capability_decl == expected_protocol_decl && moniker == expected_source_moniker
1669        );
1670    }
1671
1672    ///   a
1673    ///  / \
1674    /// b   c
1675    ///
1676    /// b: exposes directory /data/foo from self as /data/bar to framework (NOT realm)
1677    /// a: offers directory /data/bar from b as /data/baz to c, but it is not exposed via realm
1678    /// c: uses /data/baz as /data/hippo
1679    pub async fn test_use_from_expose_to_framework(&self) {
1680        let components = vec![
1681            (
1682                "a",
1683                ComponentDeclBuilder::new()
1684                    .offer(
1685                        OfferBuilder::directory()
1686                            .name("bar_data")
1687                            .target_name("baz_data")
1688                            .source_static_child("b")
1689                            .target_static_child("c")
1690                            .rights(fio::R_STAR_DIR),
1691                    )
1692                    .offer(
1693                        OfferBuilder::protocol()
1694                            .name("bar")
1695                            .target_name("baz")
1696                            .source_static_child("b")
1697                            .target_static_child("c"),
1698                    )
1699                    .child_default("b")
1700                    .child_default("c")
1701                    .build(),
1702            ),
1703            (
1704                "b",
1705                ComponentDeclBuilder::new()
1706                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
1707                    .protocol_default("foo")
1708                    .expose(
1709                        ExposeBuilder::directory()
1710                            .name("foo_data")
1711                            .source(ExposeSource::Self_)
1712                            .target_name("bar_data")
1713                            .target(ExposeTarget::Framework)
1714                            .rights(fio::R_STAR_DIR),
1715                    )
1716                    .expose(
1717                        ExposeBuilder::protocol()
1718                            .name("foo")
1719                            .target_name("bar")
1720                            .source(ExposeSource::Self_)
1721                            .target(ExposeTarget::Framework),
1722                    )
1723                    .build(),
1724            ),
1725            (
1726                "c",
1727                ComponentDeclBuilder::new()
1728                    .use_(UseBuilder::directory().name("baz_data").path("/data/hippo"))
1729                    .use_(UseBuilder::protocol().name("baz").path("/svc/hippo"))
1730                    .build(),
1731            ),
1732        ];
1733        let model = T::new("a", components).build().await;
1734        model
1735            .check_use(
1736                ["c"].try_into().unwrap(),
1737                CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1738            )
1739            .await;
1740        model
1741            .check_use(
1742                ["c"].try_into().unwrap(),
1743                CheckUse::Protocol {
1744                    path: default_service_capability(),
1745                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1746                },
1747            )
1748            .await;
1749    }
1750
1751    ///   a
1752    ///    \
1753    ///     b
1754    ///
1755    /// a: offers directory /data/hippo to b, but a is not executable
1756    /// a: offers service /svc/hippo to b, but a is not executable
1757    /// b: uses directory /data/hippo as /data/hippo, but it's not in its realm
1758    /// b: uses service /svc/hippo as /svc/hippo, but it's not in its realm
1759    pub async fn test_offer_from_non_executable(&self) {
1760        let components = vec![
1761            (
1762                "a",
1763                ComponentDeclBuilder::new_empty_component()
1764                    .capability(CapabilityBuilder::directory().name("hippo_data").path("/data"))
1765                    .protocol_default("hippo")
1766                    .offer(
1767                        OfferBuilder::directory()
1768                            .name("hippo_data")
1769                            .source(OfferSource::Self_)
1770                            .target_static_child("b")
1771                            .rights(fio::R_STAR_DIR),
1772                    )
1773                    .offer(
1774                        OfferBuilder::protocol()
1775                            .name("hippo")
1776                            .source(OfferSource::Self_)
1777                            .target_static_child("b"),
1778                    )
1779                    .child_default("b")
1780                    .build(),
1781            ),
1782            (
1783                "b",
1784                ComponentDeclBuilder::new()
1785                    .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1786                    .use_(UseBuilder::protocol().name("hippo"))
1787                    .build(),
1788            ),
1789        ];
1790        let model = T::new("a", components).build().await;
1791        model
1792            .check_use(
1793                ["b"].try_into().unwrap(),
1794                CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1795            )
1796            .await;
1797        model
1798            .check_use(
1799                ["b"].try_into().unwrap(),
1800                CheckUse::Protocol {
1801                    path: default_service_capability(),
1802                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1803                },
1804            )
1805            .await;
1806    }
1807
1808    ///   a
1809    /// / | \
1810    /// b c d
1811    ///
1812    /// a: offers "foo" from both b and c to d
1813    /// b: exposes "foo" to parent from self
1814    /// c: exposes "foo" to parent from self
1815    /// d: uses "foo" from parent
1816    /// routing an aggregate service with non-conflicting filters should succeed.
1817    pub async fn test_route_filtered_aggregate_service(&self) {
1818        let expected_service_decl = CapabilityBuilder::service().name("foo").build();
1819        let components = vec![
1820            (
1821                "a",
1822                ComponentDeclBuilder::new()
1823                    .offer(
1824                        OfferBuilder::service()
1825                            .name("foo")
1826                            .source_static_child("b")
1827                            .target_static_child("d")
1828                            .source_instance_filter(["instance_0", "instance_1"]),
1829                    )
1830                    .offer(
1831                        OfferBuilder::service()
1832                            .name("foo")
1833                            .source_static_child("c")
1834                            .target_static_child("d")
1835                            .source_instance_filter(["instance_2", "instance_3"]),
1836                    )
1837                    .child_default("b")
1838                    .child_default("c")
1839                    .child_default("d")
1840                    .build(),
1841            ),
1842            (
1843                "b",
1844                ComponentDeclBuilder::new()
1845                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
1846                    .capability(expected_service_decl.clone())
1847                    .build(),
1848            ),
1849            (
1850                "c",
1851                ComponentDeclBuilder::new()
1852                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
1853                    .capability(expected_service_decl.clone())
1854                    .build(),
1855            ),
1856            ("d", ComponentDeclBuilder::new().use_(UseBuilder::service().name("foo")).build()),
1857        ];
1858        let model = T::new("a", components).build().await;
1859
1860        let d_component =
1861            model.look_up_instance(&["d"].try_into().unwrap()).await.expect("b instance");
1862
1863        let source = debug_route_sandbox_path(
1864            &d_component,
1865            &UseDecl::Service(UseServiceDecl {
1866                source: UseSource::Parent,
1867                source_name: "foo".parse().unwrap(),
1868                source_dictionary: Default::default(),
1869                target_path: "/svc/foo".parse().unwrap(),
1870                dependency_type: DependencyType::Strong,
1871                availability: Availability::Required,
1872            }),
1873        )
1874        .await
1875        .expect("failed to route service");
1876        match source {
1877            CapabilitySource::FilteredAggregateProvider(FilteredAggregateProviderSource {
1878                capability: AggregateCapability::Service(name),
1879                ..
1880            }) => assert_eq!(name, "foo"),
1881            _ => panic!("bad capability source"),
1882        };
1883    }
1884
1885    ///   a
1886    ///   |
1887    ///   b
1888    ///  /|
1889    /// c d
1890    ///
1891    /// a: offers "foo" from self to `b`
1892    /// b: offers "foo" from parent, c, and itself to d, forming an aggregate
1893    /// c: exposes "foo" to parent from self
1894    /// d: uses "foo" from parent
1895    /// routing an aggregate service without specifying a source_instance_filter should fail.
1896    pub async fn test_route_anonymized_aggregate_service(&self) {
1897        let expected_service_decl = CapabilityBuilder::service().name("foo").build();
1898        let components = vec![
1899            (
1900                "a",
1901                ComponentDeclBuilder::new()
1902                    .offer(
1903                        OfferBuilder::service()
1904                            .name("foo")
1905                            .source(OfferSource::Self_)
1906                            .target_static_child("b"),
1907                    )
1908                    .capability(expected_service_decl.clone())
1909                    .child_default("b")
1910                    .build(),
1911            ),
1912            (
1913                "b",
1914                ComponentDeclBuilder::new()
1915                    .offer(
1916                        OfferBuilder::service()
1917                            .name("foo")
1918                            .source_static_child("c")
1919                            .target_static_child("d"),
1920                    )
1921                    .offer(
1922                        OfferBuilder::service()
1923                            .name("foo")
1924                            .source(OfferSource::Parent)
1925                            .target_static_child("d"),
1926                    )
1927                    .offer(
1928                        OfferBuilder::service()
1929                            .name("foo")
1930                            .source(OfferSource::Self_)
1931                            .target_static_child("d"),
1932                    )
1933                    .capability(expected_service_decl.clone())
1934                    .child_default("c")
1935                    .child_default("d")
1936                    .build(),
1937            ),
1938            (
1939                "c",
1940                ComponentDeclBuilder::new()
1941                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
1942                    .capability(expected_service_decl.clone())
1943                    .build(),
1944            ),
1945            ("d", ComponentDeclBuilder::new().use_(UseBuilder::service().name("foo")).build()),
1946        ];
1947        let test = T::new("a", components).build().await;
1948
1949        let d_component = test.look_up_instance(&"b/d".parse().unwrap()).await.expect("b instance");
1950        let source = debug_route_sandbox_path(
1951            &d_component,
1952            &UseDecl::Service(UseServiceDecl {
1953                source: UseSource::Parent,
1954                source_name: "foo".parse().unwrap(),
1955                source_dictionary: Default::default(),
1956                target_path: "/svc/foo".parse().unwrap(),
1957                dependency_type: DependencyType::Strong,
1958                availability: Availability::Required,
1959            }),
1960        )
1961        .await
1962        .unwrap();
1963        match source {
1964            CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
1965                capability: AggregateCapability::Service(name),
1966                members,
1967                ..
1968            }) => {
1969                assert_eq!(name, "foo");
1970                assert_eq!(members.len(), 3);
1971                for c in [
1972                    AggregateMember::Child(ChildRef {
1973                        name: "c".parse().unwrap(),
1974                        collection: None,
1975                    }),
1976                    AggregateMember::Parent,
1977                    AggregateMember::Self_,
1978                ] {
1979                    assert!(members.contains(&c));
1980                }
1981            }
1982            _ => panic!("bad capability source"),
1983        }
1984    }
1985
1986    ///   a
1987    ///    \
1988    ///     b
1989    ///      \
1990    ///       c
1991    ///
1992    /// a: offers directory /data/foo from self with subdir 's1/s2'
1993    /// b: offers directory /data/foo from realm with subdir 's3'
1994    /// c: uses /data/foo as /data/hippo
1995    pub async fn test_use_directory_with_subdir_from_grandparent(&self) {
1996        let components = vec![
1997            (
1998                "a",
1999                ComponentDeclBuilder::new()
2000                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2001                    .protocol_default("foo")
2002                    .offer(
2003                        OfferBuilder::directory()
2004                            .name("foo_data")
2005                            .source(OfferSource::Self_)
2006                            .target_static_child("b")
2007                            .rights(fio::R_STAR_DIR)
2008                            .subdir("s1/s2"),
2009                    )
2010                    .child_default("b")
2011                    .build(),
2012            ),
2013            (
2014                "b",
2015                ComponentDeclBuilder::new()
2016                    .offer(
2017                        OfferBuilder::directory()
2018                            .name("foo_data")
2019                            .source(OfferSource::Parent)
2020                            .target_static_child("c")
2021                            .rights(fio::R_STAR_DIR)
2022                            .subdir("s3"),
2023                    )
2024                    .child_default("c")
2025                    .build(),
2026            ),
2027            (
2028                "c",
2029                ComponentDeclBuilder::new()
2030                    .use_(UseBuilder::directory().name("foo_data").path("/data/hippo").subdir("s4"))
2031                    .build(),
2032            ),
2033        ];
2034        let model = T::new("a", components).build().await;
2035        model
2036            .create_static_file(Path::new("foo/s1/s2/s3/s4/inner"), "hello")
2037            .await
2038            .expect("failed to create file");
2039        model
2040            .check_use(
2041                ["b", "c"].try_into().unwrap(),
2042                CheckUse::Directory {
2043                    path: default_directory_capability(),
2044                    file: PathBuf::from("inner"),
2045                    expected_res: ExpectedResult::Ok,
2046                },
2047            )
2048            .await;
2049    }
2050
2051    ///   a
2052    ///  / \
2053    /// b   c
2054    ///
2055    ///
2056    /// b: exposes directory /data/foo from self with subdir 's1/s2'
2057    /// a: offers directory /data/foo from `b` to `c` with subdir 's3'
2058    /// c: uses /data/foo as /data/hippo
2059    pub async fn test_use_directory_with_subdir_from_sibling(&self) {
2060        let components = vec![
2061            (
2062                "a",
2063                ComponentDeclBuilder::new()
2064                    .offer(
2065                        OfferBuilder::directory()
2066                            .name("foo_data")
2067                            .source_static_child("b")
2068                            .target_static_child("c")
2069                            .rights(fio::R_STAR_DIR)
2070                            .subdir("s3"),
2071                    )
2072                    .child_default("b")
2073                    .child_default("c")
2074                    .build(),
2075            ),
2076            (
2077                "b",
2078                ComponentDeclBuilder::new()
2079                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2080                    .expose(
2081                        ExposeBuilder::directory()
2082                            .name("foo_data")
2083                            .source(ExposeSource::Self_)
2084                            .rights(fio::R_STAR_DIR)
2085                            .subdir("s1/s2"),
2086                    )
2087                    .build(),
2088            ),
2089            (
2090                "c",
2091                ComponentDeclBuilder::new()
2092                    .use_(UseBuilder::directory().name("foo_data").path("/data/hippo"))
2093                    .build(),
2094            ),
2095        ];
2096        let model = T::new("a", components).build().await;
2097        model
2098            .create_static_file(Path::new("foo/s1/s2/s3/inner"), "hello")
2099            .await
2100            .expect("failed to create file");
2101        model
2102            .check_use(
2103                ["c"].try_into().unwrap(),
2104                CheckUse::Directory {
2105                    path: default_directory_capability(),
2106                    file: PathBuf::from("inner"),
2107                    expected_res: ExpectedResult::Ok,
2108                },
2109            )
2110            .await;
2111    }
2112
2113    ///   a
2114    ///    \
2115    ///     b
2116    ///      \
2117    ///       c
2118    ///
2119    /// c: exposes /data/foo from self
2120    /// b: exposes /data/foo from `c` with subdir `s1/s2`
2121    /// a: exposes /data/foo from `b` with subdir `s3` as /data/hippo
2122    /// use /data/hippo from a's exposed dir
2123    pub async fn test_expose_directory_with_subdir(&self) {
2124        let components = vec![
2125            (
2126                "a",
2127                ComponentDeclBuilder::new()
2128                    .expose(
2129                        ExposeBuilder::directory()
2130                            .name("foo_data")
2131                            .source_static_child("b")
2132                            .target_name("hippo_data")
2133                            .subdir("s3"),
2134                    )
2135                    .child_default("b")
2136                    .build(),
2137            ),
2138            (
2139                "b",
2140                ComponentDeclBuilder::new()
2141                    .expose(
2142                        ExposeBuilder::directory()
2143                            .name("foo_data")
2144                            .source_static_child("c")
2145                            .subdir("s1/s2"),
2146                    )
2147                    .child_default("c")
2148                    .build(),
2149            ),
2150            (
2151                "c",
2152                ComponentDeclBuilder::new()
2153                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2154                    .expose(
2155                        ExposeBuilder::directory()
2156                            .name("foo_data")
2157                            .source(ExposeSource::Self_)
2158                            .rights(fio::R_STAR_DIR),
2159                    )
2160                    .build(),
2161            ),
2162        ];
2163        let model = T::new("a", components).build().await;
2164        model
2165            .create_static_file(Path::new("foo/s1/s2/s3/inner"), "hello")
2166            .await
2167            .expect("failed to create file");
2168        model
2169            .check_use_exposed_dir(
2170                Moniker::root(),
2171                CheckUse::Directory {
2172                    path: "/hippo_data".parse().unwrap(),
2173                    file: PathBuf::from("inner"),
2174                    expected_res: ExpectedResult::Ok,
2175                },
2176            )
2177            .await;
2178    }
2179
2180    pub async fn test_expose_from_self_and_child(&self) {
2181        let components = vec![
2182            ("a", ComponentDeclBuilder::new().child_default("b").build()),
2183            (
2184                "b",
2185                ComponentDeclBuilder::new()
2186                    .expose(
2187                        ExposeBuilder::directory()
2188                            .name("hippo_data")
2189                            .source_static_child("c")
2190                            .target_name("hippo_bar_data")
2191                            .rights(fio::R_STAR_DIR),
2192                    )
2193                    .expose(
2194                        ExposeBuilder::protocol()
2195                            .name("hippo")
2196                            .target_name("hippo_bar")
2197                            .source_static_child("c"),
2198                    )
2199                    .child_default("c")
2200                    .build(),
2201            ),
2202            (
2203                "c",
2204                ComponentDeclBuilder::new()
2205                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2206                    .protocol_default("foo")
2207                    .expose(
2208                        ExposeBuilder::directory()
2209                            .name("foo_data")
2210                            .source(ExposeSource::Self_)
2211                            .target_name("hippo_data")
2212                            .rights(fio::R_STAR_DIR),
2213                    )
2214                    .expose(
2215                        ExposeBuilder::protocol()
2216                            .name("foo")
2217                            .target_name("hippo")
2218                            .source(ExposeSource::Self_),
2219                    )
2220                    .build(),
2221            ),
2222        ];
2223        let model = T::new("a", components).build().await;
2224        model
2225            .check_use_exposed_dir(
2226                ["b"].try_into().unwrap(),
2227                CheckUse::Directory {
2228                    path: "/hippo_bar_data".parse().unwrap(),
2229                    file: PathBuf::from("hippo"),
2230                    expected_res: ExpectedResult::Ok,
2231                },
2232            )
2233            .await;
2234        model
2235            .check_use_exposed_dir(
2236                ["b"].try_into().unwrap(),
2237                CheckUse::Protocol {
2238                    path: "/hippo_bar".parse().unwrap(),
2239                    expected_res: ExpectedResult::Ok,
2240                },
2241            )
2242            .await;
2243        model
2244            .check_use_exposed_dir(
2245                ["b", "c"].try_into().unwrap(),
2246                CheckUse::Directory {
2247                    path: "/hippo_data".parse().unwrap(),
2248                    file: PathBuf::from("hippo"),
2249                    expected_res: ExpectedResult::Ok,
2250                },
2251            )
2252            .await;
2253        model
2254            .check_use_exposed_dir(
2255                ["b", "c"].try_into().unwrap(),
2256                CheckUse::Protocol {
2257                    path: "/hippo".parse().unwrap(),
2258                    expected_res: ExpectedResult::Ok,
2259                },
2260            )
2261            .await;
2262    }
2263
2264    pub async fn test_use_not_exposed(&self) {
2265        let components = vec![
2266            ("a", ComponentDeclBuilder::new().child_default("b").build()),
2267            ("b", ComponentDeclBuilder::new().child_default("c").build()),
2268            (
2269                "c",
2270                ComponentDeclBuilder::new()
2271                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2272                    .protocol_default("foo")
2273                    .expose(
2274                        ExposeBuilder::directory()
2275                            .name("foo_data")
2276                            .source(ExposeSource::Self_)
2277                            .target_name("hippo_data")
2278                            .rights(fio::R_STAR_DIR),
2279                    )
2280                    .expose(
2281                        ExposeBuilder::protocol()
2282                            .name("foo")
2283                            .target_name("hippo")
2284                            .source(ExposeSource::Self_),
2285                    )
2286                    .build(),
2287            ),
2288        ];
2289        let model = T::new("a", components).build().await;
2290        // Capability is only exposed from "c", so it only be usable from there.
2291
2292        // When trying to open a capability that's not exposed to realm, there's no node for it in the
2293        // exposed dir, so no routing takes place.
2294        model
2295            .check_use_exposed_dir(
2296                ["b"].try_into().unwrap(),
2297                CheckUse::Directory {
2298                    path: "/hippo_data".parse().unwrap(),
2299                    file: PathBuf::from("hippo"),
2300                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2301                },
2302            )
2303            .await;
2304        model
2305            .check_use_exposed_dir(
2306                ["b"].try_into().unwrap(),
2307                CheckUse::Protocol {
2308                    path: "/hippo".parse().unwrap(),
2309                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2310                },
2311            )
2312            .await;
2313        model
2314            .check_use_exposed_dir(
2315                ["b", "c"].try_into().unwrap(),
2316                CheckUse::Directory {
2317                    path: "/hippo_data".parse().unwrap(),
2318                    file: PathBuf::from("hippo"),
2319                    expected_res: ExpectedResult::Ok,
2320                },
2321            )
2322            .await;
2323        model
2324            .check_use_exposed_dir(
2325                ["b", "c"].try_into().unwrap(),
2326                CheckUse::Protocol {
2327                    path: "/hippo".parse().unwrap(),
2328                    expected_res: ExpectedResult::Ok,
2329                },
2330            )
2331            .await;
2332    }
2333
2334    pub async fn test_expose_to_framework_from_self(&self) {
2335        let components = vec![(
2336            "a",
2337            ComponentDeclBuilder::new()
2338                .protocol_default("foo")
2339                .expose(
2340                    ExposeBuilder::protocol()
2341                        .name("foo")
2342                        .target_name("hippo")
2343                        .target(ExposeTarget::Framework)
2344                        .source(ExposeSource::Self_),
2345                )
2346                .build(),
2347        )];
2348        let model = T::new("a", components).build().await;
2349        model
2350            .check_exposed_to_framework(
2351                Moniker::root(),
2352                CheckUse::Protocol {
2353                    path: "/hippo".parse().unwrap(),
2354                    expected_res: ExpectedResult::Ok,
2355                },
2356            )
2357            .await;
2358    }
2359
2360    pub async fn test_expose_to_framework_from_child(&self) {
2361        let components = vec![
2362            (
2363                "a",
2364                ComponentDeclBuilder::new()
2365                    .child_default("b")
2366                    .expose(
2367                        ExposeBuilder::protocol()
2368                            .name("foo")
2369                            .target_name("hippo")
2370                            .target(ExposeTarget::Framework)
2371                            .source(ExposeSource::Child("b".parse().unwrap())),
2372                    )
2373                    .build(),
2374            ),
2375            (
2376                "b",
2377                ComponentDeclBuilder::new()
2378                    .protocol_default("foo")
2379                    .expose(ExposeBuilder::protocol().name("foo").source(ExposeSource::Self_))
2380                    .build(),
2381            ),
2382        ];
2383        let model = T::new("a", components).build().await;
2384        model
2385            .check_exposed_to_framework(
2386                Moniker::root(),
2387                CheckUse::Protocol {
2388                    path: "/hippo".parse().unwrap(),
2389                    expected_res: ExpectedResult::Ok,
2390                },
2391            )
2392            .await;
2393    }
2394
2395    pub async fn test_expose_to_parent_and_framework(&self) {
2396        let components = vec![
2397            ("a", ComponentDeclBuilder::new().child_default("b").build()),
2398            (
2399                "b",
2400                ComponentDeclBuilder::new()
2401                    .protocol_default("foo")
2402                    .expose(
2403                        ExposeBuilder::protocol()
2404                            .name("foo")
2405                            .target_name("hippo")
2406                            .source(ExposeSource::Self_),
2407                    )
2408                    .expose(
2409                        ExposeBuilder::protocol()
2410                            .name("foo")
2411                            .target(ExposeTarget::Framework)
2412                            .source(ExposeSource::Self_),
2413                    )
2414                    .build(),
2415            ),
2416        ];
2417        let model = T::new("a", components).build().await;
2418        model
2419            .check_exposed_to_framework(
2420                ["b"].try_into().unwrap(),
2421                CheckUse::Protocol {
2422                    path: "/hippo".parse().unwrap(),
2423                    expected_res: ExpectedResult::Ok,
2424                },
2425            )
2426            .await;
2427        model
2428            .check_use_exposed_dir(
2429                ["b"].try_into().unwrap(),
2430                CheckUse::Protocol {
2431                    path: "/hippo".parse().unwrap(),
2432                    expected_res: ExpectedResult::Ok,
2433                },
2434            )
2435            .await;
2436    }
2437
2438    ///   (cm)
2439    ///    |
2440    ///    a
2441    ///
2442    /// a: uses an invalid service from the component manager.
2443    pub async fn test_invalid_use_from_component_manager(&self) {
2444        let components = vec![(
2445            "a",
2446            ComponentDeclBuilder::new()
2447                .use_(UseBuilder::protocol().name("invalid").path("/svc/valid"))
2448                .build(),
2449        )];
2450
2451        // Try and use the service. We expect a failure.
2452        let model = T::new("a", components).build().await;
2453        model
2454            .check_use(
2455                Moniker::root(),
2456                CheckUse::Protocol {
2457                    path: "/svc/valid".parse().unwrap(),
2458                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2459                },
2460            )
2461            .await;
2462    }
2463
2464    ///   (cm)
2465    ///    |
2466    ///    a
2467    ///    |
2468    ///    b
2469    ///
2470    /// a: offers an invalid service from the component manager to "b".
2471    /// b: attempts to use the service
2472    pub async fn test_invalid_offer_from_component_manager(&self) {
2473        let components = vec![
2474            (
2475                "a",
2476                ComponentDeclBuilder::new()
2477                    .offer(
2478                        OfferBuilder::protocol()
2479                            .name("invalid")
2480                            .target_name("valid")
2481                            .source(OfferSource::Parent)
2482                            .target_static_child("b"),
2483                    )
2484                    .child_default("b")
2485                    .build(),
2486            ),
2487            ("b", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("valid")).build()),
2488        ];
2489
2490        // Try and use the service. We expect a failure.
2491        let model = T::new("a", components).build().await;
2492        model
2493            .check_use(
2494                ["b"].try_into().unwrap(),
2495                CheckUse::Protocol {
2496                    path: "/svc/valid".parse().unwrap(),
2497                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2498                },
2499            )
2500            .await;
2501    }
2502
2503    /// Tests event stream aliasing (scoping rules are applied correctly)
2504    ///        root
2505    ///        |
2506    ///        a
2507    ///       /|\
2508    ///      b c d
2509    /// A offers started to b with scope b, A offers started to c with scope d,
2510    /// A offers started to d with scope c.
2511    pub async fn test_event_stream_aliasing(&self) {
2512        let components = vec![
2513            ("root", ComponentDeclBuilder::new().child_default("a").build()),
2514            (
2515                "a",
2516                ComponentDeclBuilder::new()
2517                    .offer(
2518                        OfferBuilder::event_stream()
2519                            .name("started")
2520                            .source(OfferSource::Parent)
2521                            .target(OfferTarget::Child(ChildRef {
2522                                name: "b".parse().unwrap(),
2523                                collection: None,
2524                            }))
2525                            .scope(vec![EventScope::Child(ChildRef {
2526                                name: "b".parse().unwrap(),
2527                                collection: None,
2528                            })]),
2529                    )
2530                    .offer(
2531                        OfferBuilder::event_stream()
2532                            .name("started")
2533                            .source(OfferSource::Parent)
2534                            .target(OfferTarget::Child(ChildRef {
2535                                name: "d".parse().unwrap(),
2536                                collection: None,
2537                            }))
2538                            .scope(vec![EventScope::Child(ChildRef {
2539                                name: "c".parse().unwrap(),
2540                                collection: None,
2541                            })]),
2542                    )
2543                    .offer(
2544                        OfferBuilder::event_stream()
2545                            .name("started")
2546                            .source(OfferSource::Parent)
2547                            .target(OfferTarget::Child(ChildRef {
2548                                name: "c".parse().unwrap(),
2549                                collection: None,
2550                            }))
2551                            .scope(vec![EventScope::Child(ChildRef {
2552                                name: "d".parse().unwrap(),
2553                                collection: None,
2554                            })]),
2555                    )
2556                    .child_default("b")
2557                    .child_default("c")
2558                    .child_default("d")
2559                    .build(),
2560            ),
2561            (
2562                "b",
2563                ComponentDeclBuilder::new()
2564                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2565                    .build(),
2566            ),
2567            (
2568                "c",
2569                ComponentDeclBuilder::new()
2570                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2571                    .build(),
2572            ),
2573            (
2574                "d",
2575                ComponentDeclBuilder::new()
2576                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2577                    .build(),
2578            ),
2579        ];
2580
2581        let mut builder = T::new("a", components);
2582        builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2583            name: "started".parse().unwrap(),
2584        })]);
2585
2586        let model = builder.build().await;
2587        model
2588            .check_use(
2589                ["b"].try_into().unwrap(),
2590                CheckUse::EventStream {
2591                    expected_res: ExpectedResult::Ok,
2592                    path: "/event/stream".parse().unwrap(),
2593                    scope: Some(ComponentEventRoute {
2594                        component: ".".to_string(),
2595                        scope: Some(vec!["b".to_string()]),
2596                    }),
2597                    name: "started".parse().unwrap(),
2598                },
2599            )
2600            .await;
2601        model
2602            .check_use(
2603                ["c"].try_into().unwrap(),
2604                CheckUse::EventStream {
2605                    expected_res: ExpectedResult::Ok,
2606                    path: "/event/stream".parse().unwrap(),
2607                    scope: Some(ComponentEventRoute {
2608                        component: ".".to_string(),
2609                        scope: Some(vec!["d".to_string()]),
2610                    }),
2611                    name: "started".parse().unwrap(),
2612                },
2613            )
2614            .await;
2615
2616        model
2617            .check_use(
2618                ["d"].try_into().unwrap(),
2619                CheckUse::EventStream {
2620                    expected_res: ExpectedResult::Ok,
2621                    path: "/event/stream".parse().unwrap(),
2622                    scope: Some(ComponentEventRoute {
2623                        component: ".".to_string(),
2624                        scope: Some(vec!["c".to_string()]),
2625                    }),
2626                    name: "started".parse().unwrap(),
2627                },
2628            )
2629            .await;
2630    }
2631
2632    ///   a
2633    ///    \
2634    ///     b
2635    ///
2636    /// b: uses framework events "started", and "capability_requested"
2637    pub async fn test_use_event_stream_from_above_root(&self) {
2638        let components = vec![(
2639            "a",
2640            ComponentDeclBuilder::new()
2641                .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2642                .build(),
2643        )];
2644
2645        let mut builder = T::new("a", components);
2646        builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2647            name: "started".parse().unwrap(),
2648        })]);
2649
2650        let model = builder.build().await;
2651        model
2652            .check_use(
2653                Moniker::root(),
2654                CheckUse::EventStream {
2655                    expected_res: ExpectedResult::Ok,
2656                    path: "/event/stream".parse().unwrap(),
2657                    scope: None,
2658                    name: "started".parse().unwrap(),
2659                },
2660            )
2661            .await;
2662    }
2663
2664    ///   a
2665    ///   /\
2666    ///  b  c
2667    ///    / \
2668    ///   d   e
2669    /// c: uses framework events "started", and "capability_requested",
2670    /// scoped to b and c.
2671    /// d receives started which is scoped to b, c, and e.
2672    pub async fn test_use_event_stream_from_above_root_and_downscoped(&self) {
2673        let components = vec![
2674            (
2675                "a",
2676                ComponentDeclBuilder::new()
2677                    .offer(
2678                        OfferBuilder::event_stream()
2679                            .name("started")
2680                            .source(OfferSource::Parent)
2681                            .target(OfferTarget::Child(ChildRef {
2682                                name: "b".parse().unwrap(),
2683                                collection: None,
2684                            }))
2685                            .scope(vec![
2686                                EventScope::Child(ChildRef {
2687                                    name: "b".parse().unwrap(),
2688                                    collection: None,
2689                                }),
2690                                EventScope::Child(ChildRef {
2691                                    name: "c".parse().unwrap(),
2692                                    collection: None,
2693                                }),
2694                            ]),
2695                    )
2696                    .offer(
2697                        OfferBuilder::event_stream()
2698                            .name("started")
2699                            .source(OfferSource::Parent)
2700                            .target(OfferTarget::Child(ChildRef {
2701                                name: "c".parse().unwrap(),
2702                                collection: None,
2703                            }))
2704                            .scope(vec![
2705                                EventScope::Child(ChildRef {
2706                                    name: "b".parse().unwrap(),
2707                                    collection: None,
2708                                }),
2709                                EventScope::Child(ChildRef {
2710                                    name: "c".parse().unwrap(),
2711                                    collection: None,
2712                                }),
2713                            ]),
2714                    )
2715                    .child_default("b")
2716                    .child_default("c")
2717                    .build(),
2718            ),
2719            (
2720                "b",
2721                ComponentDeclBuilder::new()
2722                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2723                    .build(),
2724            ),
2725            (
2726                "c",
2727                ComponentDeclBuilder::new()
2728                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2729                    .offer(
2730                        OfferBuilder::event_stream()
2731                            .name("started")
2732                            .source(OfferSource::Parent)
2733                            .target(OfferTarget::Child(ChildRef {
2734                                name: "d".parse().unwrap(),
2735                                collection: None,
2736                            }))
2737                            .scope(vec![EventScope::Child(ChildRef {
2738                                name: "e".parse().unwrap(),
2739                                collection: None,
2740                            })]),
2741                    )
2742                    .child_default("d")
2743                    .child_default("e")
2744                    .build(),
2745            ),
2746            (
2747                "d",
2748                ComponentDeclBuilder::new()
2749                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2750                    .build(),
2751            ),
2752            ("e", ComponentDeclBuilder::new().build()),
2753        ];
2754
2755        let mut builder = T::new("a", components);
2756        builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2757            name: "started".parse().unwrap(),
2758        })]);
2759
2760        let model = builder.build().await;
2761        model
2762            .check_use(
2763                ["b"].try_into().unwrap(),
2764                CheckUse::EventStream {
2765                    expected_res: ExpectedResult::Ok,
2766                    path: "/event/stream".parse().unwrap(),
2767                    scope: Some(ComponentEventRoute {
2768                        component: ".".to_string(),
2769                        scope: Some(vec!["b".to_string(), "c".to_string()]),
2770                    }),
2771                    name: "started".parse().unwrap(),
2772                },
2773            )
2774            .await;
2775        model
2776            .check_use(
2777                ["c"].try_into().unwrap(),
2778                CheckUse::EventStream {
2779                    expected_res: ExpectedResult::Ok,
2780                    path: "/event/stream".parse().unwrap(),
2781                    scope: Some(ComponentEventRoute {
2782                        component: ".".to_string(),
2783                        scope: Some(vec!["b".to_string(), "c".to_string()]),
2784                    }),
2785                    name: "started".parse().unwrap(),
2786                },
2787            )
2788            .await;
2789        model
2790            .check_use(
2791                ["c", "d"].try_into().unwrap(), // Should get e's event from parent
2792                CheckUse::EventStream {
2793                    expected_res: ExpectedResult::Ok,
2794                    path: "/event/stream".parse().unwrap(),
2795                    scope: Some(ComponentEventRoute {
2796                        component: "c".to_string(),
2797                        scope: Some(vec!["e".to_string()]),
2798                    }),
2799                    name: "started".parse().unwrap(),
2800                },
2801            )
2802            .await;
2803    }
2804
2805    ///   a
2806    ///    \
2807    ///     b
2808    ///
2809    /// a; attempts to offer event "capability_requested" to b.
2810    pub async fn test_can_offer_capability_requested_event(&self) {
2811        let components = vec![
2812            (
2813                "a",
2814                ComponentDeclBuilder::new()
2815                    .offer(
2816                        OfferBuilder::event_stream()
2817                            .name("capability_requested")
2818                            .target_name("capability_requested_on_a")
2819                            .source(OfferSource::Parent)
2820                            .target_static_child("b"),
2821                    )
2822                    .child_default("b")
2823                    .build(),
2824            ),
2825            (
2826                "b",
2827                ComponentDeclBuilder::new()
2828                    .use_(UseBuilder::event_stream().name("capability_requested_on_a"))
2829                    .build(),
2830            ),
2831        ];
2832
2833        let mut builder = T::new("a", components);
2834        builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2835            name: "capability_requested".parse().unwrap(),
2836        })]);
2837        let model = builder.build().await;
2838
2839        model
2840            .check_use(
2841                ["b"].try_into().unwrap(),
2842                CheckUse::EventStream {
2843                    expected_res: ExpectedResult::Ok,
2844                    path: "/svc/fuchsia.component.EventStream".parse().unwrap(),
2845                    scope: Some(ComponentEventRoute { component: ".".to_string(), scope: None }),
2846                    name: "capability_requested_on_a".parse().unwrap(),
2847                },
2848            )
2849            .await;
2850    }
2851
2852    ///   a
2853    ///    \
2854    ///     b
2855    ///
2856    /// b: uses service /svc/hippo as /svc/hippo.
2857    /// a: provides b with the service but policy prevents it.
2858    pub async fn test_use_protocol_denied_by_capability_policy(&self) {
2859        let components = vec![
2860            (
2861                "a",
2862                ComponentDeclBuilder::new()
2863                    .protocol_default("hippo")
2864                    .offer(
2865                        OfferBuilder::protocol()
2866                            .name("hippo")
2867                            .source(OfferSource::Self_)
2868                            .target_static_child("b"),
2869                    )
2870                    .child_default("b")
2871                    .build(),
2872            ),
2873            ("b", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
2874        ];
2875        let mut builder = T::new("a", components);
2876        builder.add_capability_policy(
2877            CapabilityAllowlistKey {
2878                source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
2879                source_name: "hippo".parse().unwrap(),
2880                source: CapabilityAllowlistSource::Self_,
2881                capability: CapabilityTypeName::Protocol,
2882            },
2883            HashSet::new(),
2884        );
2885
2886        let model = builder.build().await;
2887        model
2888            .check_use(
2889                ["b"].try_into().unwrap(),
2890                CheckUse::Protocol {
2891                    path: default_service_capability(),
2892                    expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
2893                },
2894            )
2895            .await;
2896    }
2897
2898    ///   a
2899    ///    \
2900    ///     b
2901    ///
2902    /// b: uses directory /data/foo as /data/bar.
2903    /// a: provides b with the directory but policy prevents it.
2904    pub async fn test_use_directory_with_alias_denied_by_capability_policy(&self) {
2905        let components = vec![
2906            (
2907                "a",
2908                ComponentDeclBuilder::new()
2909                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2910                    .offer(
2911                        OfferBuilder::directory()
2912                            .name("foo_data")
2913                            .target_name("bar_data")
2914                            .source(OfferSource::Self_)
2915                            .target_static_child("b")
2916                            .rights(fio::R_STAR_DIR),
2917                    )
2918                    .child_default("b")
2919                    .build(),
2920            ),
2921            (
2922                "b",
2923                ComponentDeclBuilder::new()
2924                    .use_(UseBuilder::directory().name("bar_data").path("/data/hippo"))
2925                    .build(),
2926            ),
2927        ];
2928        let mut builder = T::new("a", components);
2929        builder.add_capability_policy(
2930            CapabilityAllowlistKey {
2931                source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
2932                source_name: "foo_data".parse().unwrap(),
2933                source: CapabilityAllowlistSource::Self_,
2934                capability: CapabilityTypeName::Directory,
2935            },
2936            HashSet::new(),
2937        );
2938        let model = builder.build().await;
2939        model
2940            .check_use(
2941                ["b"].try_into().unwrap(),
2942                CheckUse::default_directory(ExpectedResult::Err(zx::Status::ACCESS_DENIED)),
2943            )
2944            .await;
2945    }
2946
2947    ///   a
2948    ///    \
2949    ///     b
2950    ///      \
2951    ///       c
2952    /// c: uses service /svc/hippo as /svc/hippo.
2953    /// b: uses service /svc/hippo as /svc/hippo.
2954    /// a: provides b with the service policy allows it.
2955    /// b: provides c with the service policy does not allow it.
2956    pub async fn test_use_protocol_partial_chain_allowed_by_capability_policy(&self) {
2957        let components = vec![
2958            (
2959                "a",
2960                ComponentDeclBuilder::new()
2961                    .protocol_default("hippo")
2962                    .offer(
2963                        OfferBuilder::protocol()
2964                            .name("hippo")
2965                            .source(OfferSource::Self_)
2966                            .target_static_child("b"),
2967                    )
2968                    .child_default("b")
2969                    .build(),
2970            ),
2971            (
2972                "b",
2973                ComponentDeclBuilder::new()
2974                    .offer(
2975                        OfferBuilder::protocol()
2976                            .name("hippo")
2977                            .source(OfferSource::Parent)
2978                            .target_static_child("c"),
2979                    )
2980                    .use_(UseBuilder::protocol().name("hippo"))
2981                    .child_default("c")
2982                    .build(),
2983            ),
2984            ("c", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
2985        ];
2986
2987        let mut allowlist = HashSet::new();
2988        allowlist.insert(AllowlistEntryBuilder::new().exact("b").build());
2989
2990        let mut builder = T::new("a", components);
2991        builder.add_capability_policy(
2992            CapabilityAllowlistKey {
2993                source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
2994                source_name: "hippo".parse().unwrap(),
2995                source: CapabilityAllowlistSource::Self_,
2996                capability: CapabilityTypeName::Protocol,
2997            },
2998            allowlist,
2999        );
3000        let model = builder.build().await;
3001
3002        model
3003            .check_use(
3004                ["b"].try_into().unwrap(),
3005                CheckUse::Protocol {
3006                    path: default_service_capability(),
3007                    expected_res: ExpectedResult::Ok,
3008                },
3009            )
3010            .await;
3011
3012        model
3013            .check_use(
3014                ["b", "c"].try_into().unwrap(),
3015                CheckUse::Protocol {
3016                    path: default_service_capability(),
3017                    expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
3018                },
3019            )
3020            .await;
3021    }
3022
3023    ///   a
3024    ///    \
3025    ///     b
3026    ///    /  \
3027    ///   c    d
3028    /// b: provides d with the service policy allows denies it.
3029    /// b: provides c with the service policy allows it.
3030    /// c: uses service /svc/hippo as /svc/hippo.
3031    /// Tests component provided caps in the middle of a path
3032    pub async fn test_use_protocol_component_provided_capability_policy(&self) {
3033        let components = vec![
3034            (
3035                "a",
3036                ComponentDeclBuilder::new()
3037                    .protocol_default("hippo")
3038                    .offer(
3039                        OfferBuilder::protocol()
3040                            .name("hippo")
3041                            .source(OfferSource::Self_)
3042                            .target_static_child("b"),
3043                    )
3044                    .child_default("b")
3045                    .build(),
3046            ),
3047            (
3048                "b",
3049                ComponentDeclBuilder::new()
3050                    .offer(
3051                        OfferBuilder::protocol()
3052                            .name("hippo")
3053                            .source(OfferSource::Parent)
3054                            .target_static_child("c"),
3055                    )
3056                    .offer(
3057                        OfferBuilder::protocol()
3058                            .name("hippo")
3059                            .source(OfferSource::Parent)
3060                            .target_static_child("d"),
3061                    )
3062                    .child_default("c")
3063                    .child_default("d")
3064                    .build(),
3065            ),
3066            ("c", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
3067            ("d", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
3068        ];
3069
3070        let mut allowlist = HashSet::new();
3071        allowlist.insert(AllowlistEntryBuilder::new().exact("b").build());
3072        allowlist.insert(AllowlistEntryBuilder::new().exact("b").exact("c").build());
3073
3074        let mut builder = T::new("a", components);
3075        builder.add_capability_policy(
3076            CapabilityAllowlistKey {
3077                source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
3078                source_name: "hippo".parse().unwrap(),
3079                source: CapabilityAllowlistSource::Self_,
3080                capability: CapabilityTypeName::Protocol,
3081            },
3082            allowlist,
3083        );
3084        let model = builder.build().await;
3085
3086        model
3087            .check_use(
3088                ["b", "c"].try_into().unwrap(),
3089                CheckUse::Protocol {
3090                    path: default_service_capability(),
3091                    expected_res: ExpectedResult::Ok,
3092                },
3093            )
3094            .await;
3095
3096        model
3097            .check_use(
3098                ["b", "d"].try_into().unwrap(),
3099                CheckUse::Protocol {
3100                    path: default_service_capability(),
3101                    expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
3102                },
3103            )
3104            .await;
3105    }
3106
3107    ///  component manager's namespace
3108    ///   |
3109    ///   a
3110    ///
3111    /// a: uses service /use_from_cm_namespace/svc/foo as foo
3112    pub async fn test_use_from_component_manager_namespace_denied_by_policy(&self) {
3113        let components = vec![(
3114            "a",
3115            ComponentDeclBuilder::new()
3116                .use_(UseBuilder::protocol().name("foo").path("/svc/hippo"))
3117                .build(),
3118        )];
3119        let namespace_capabilities = vec![
3120            CapabilityBuilder::protocol()
3121                .name("foo")
3122                .path("/use_from_cm_namespace/svc/foo")
3123                .build(),
3124        ];
3125        let mut builder = T::new("a", components);
3126        builder.set_namespace_capabilities(namespace_capabilities);
3127        builder.add_capability_policy(
3128            CapabilityAllowlistKey {
3129                source_moniker: ExtendedMoniker::ComponentManager,
3130                source_name: "foo".parse().unwrap(),
3131                source: CapabilityAllowlistSource::Self_,
3132                capability: CapabilityTypeName::Protocol,
3133            },
3134            HashSet::new(),
3135        );
3136        let model = builder.build().await;
3137
3138        model.install_namespace_directory("/use_from_cm_namespace");
3139        model
3140            .check_use(
3141                Moniker::root(),
3142                CheckUse::Protocol {
3143                    path: default_service_capability(),
3144                    expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
3145                },
3146            )
3147            .await;
3148    }
3149
3150    ///   a
3151    ///  /
3152    /// b
3153    ///
3154    /// a: offer to b from self
3155    /// b: use from parent
3156    pub async fn test_route_service_from_parent(&self) {
3157        let use_decl = UseBuilder::service().name("foo").build();
3158        let components = vec![
3159            (
3160                "a",
3161                ComponentDeclBuilder::new()
3162                    .offer(
3163                        OfferBuilder::service()
3164                            .name("foo")
3165                            .source(OfferSource::Self_)
3166                            .target_static_child("b"),
3167                    )
3168                    .service_default("foo")
3169                    .child_default("b")
3170                    .build(),
3171            ),
3172            ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3173        ];
3174        let model = T::new("a", components).build().await;
3175        let b_component =
3176            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3177        let a_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
3178        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3179        let source = debug_route_sandbox_path(&b_component, &UseDecl::Service(use_decl))
3180            .await
3181            .expect("failed to route service");
3182        match source {
3183            CapabilitySource::Component(ComponentSource {
3184                capability: ComponentCapability::Service(ServiceDecl { name, source_path }),
3185                moniker,
3186            }) => {
3187                assert_eq!(name, "foo");
3188                assert_eq!(
3189                    source_path.expect("missing source path"),
3190                    "/svc/foo".parse::<cm_types::Path>().unwrap()
3191                );
3192                assert_eq!(&moniker, a_component.moniker());
3193            }
3194            _ => panic!("bad capability source"),
3195        };
3196    }
3197
3198    ///   a
3199    ///  /
3200    /// b
3201    ///
3202    /// a: use from #b
3203    /// b: expose to parent from self
3204    pub async fn test_route_service_from_child(&self) {
3205        let use_decl = UseBuilder::service().name("foo").source_static_child("b").build();
3206        let components = vec![
3207            ("a", ComponentDeclBuilder::new().use_(use_decl.clone()).child_default("b").build()),
3208            (
3209                "b",
3210                ComponentDeclBuilder::new()
3211                    .service_default("foo")
3212                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3213                    .build(),
3214            ),
3215        ];
3216        let model = T::new("a", components).build().await;
3217        let a_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
3218        let b_component =
3219            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3220        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3221        let source = debug_route_sandbox_path(&a_component, &UseDecl::Service(use_decl))
3222            .await
3223            .expect("failed to route service");
3224        match source {
3225            CapabilitySource::Component(ComponentSource {
3226                capability: ComponentCapability::Service(ServiceDecl { name, source_path }),
3227                moniker,
3228            }) => {
3229                assert_eq!(name, "foo");
3230                assert_eq!(
3231                    source_path.expect("missing source path"),
3232                    "/svc/foo".parse::<cm_types::Path>().unwrap()
3233                );
3234                assert_eq!(&moniker, b_component.moniker());
3235            }
3236            _ => panic!("bad capability source"),
3237        };
3238    }
3239
3240    ///   a
3241    ///  / \
3242    /// b   c
3243    ///
3244    /// a: offer to b from child c
3245    /// b: use from parent
3246    /// c: expose from self
3247    pub async fn test_route_service_from_sibling(&self) {
3248        let use_decl = UseBuilder::service().name("foo").build();
3249        let components = vec![
3250            (
3251                "a",
3252                ComponentDeclBuilder::new()
3253                    .offer(
3254                        OfferBuilder::service()
3255                            .name("foo")
3256                            .source_static_child("c")
3257                            .target_static_child("b"),
3258                    )
3259                    .child_default("b")
3260                    .child_default("c")
3261                    .build(),
3262            ),
3263            ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3264            (
3265                "c",
3266                ComponentDeclBuilder::new()
3267                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3268                    .service_default("foo")
3269                    .build(),
3270            ),
3271        ];
3272        let model = T::new("a", components).build().await;
3273        let b_component =
3274            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3275        let c_component =
3276            model.look_up_instance(&["c"].try_into().unwrap()).await.expect("c instance");
3277        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3278        let source = debug_route_sandbox_path(&b_component, &UseDecl::Service(use_decl))
3279            .await
3280            .expect("failed to route service");
3281
3282        // Verify this source comes from `c`.
3283        match source {
3284            CapabilitySource::Component(ComponentSource {
3285                capability: ComponentCapability::Service(ServiceDecl { name, source_path }),
3286                moniker,
3287            }) => {
3288                assert_eq!(name, "foo");
3289                assert_eq!(
3290                    source_path.expect("missing source path"),
3291                    "/svc/foo".parse::<cm_types::Path>().unwrap()
3292                );
3293                assert_eq!(&moniker, c_component.moniker());
3294            }
3295            _ => panic!("bad capability source"),
3296        };
3297    }
3298
3299    ///   a
3300    ///  / \
3301    /// b   c
3302    ///
3303    /// a: offer to b with service instance filter set from child c
3304    /// b: use from parent
3305    /// c: expose from self
3306    pub async fn test_route_filtered_service_from_sibling(&self) {
3307        let use_decl = UseBuilder::service().name("foo").build();
3308        let components = vec![
3309            (
3310                "a",
3311                ComponentDeclBuilder::new()
3312                    .offer(
3313                        OfferBuilder::service()
3314                            .name("foo")
3315                            .source_static_child("c")
3316                            .target_static_child("b")
3317                            .source_instance_filter(["service_instance_0"])
3318                            .renamed_instances([]),
3319                    )
3320                    .child_default("b")
3321                    .child_default("c")
3322                    .build(),
3323            ),
3324            ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3325            (
3326                "c",
3327                ComponentDeclBuilder::new()
3328                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3329                    .service_default("foo")
3330                    .build(),
3331            ),
3332        ];
3333        let model = T::new("a", components).build().await;
3334        let b_component =
3335            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3336        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3337        let source = debug_route_sandbox_path(&b_component, &UseDecl::Service(use_decl))
3338            .await
3339            .expect("failed to route service");
3340
3341        // Verify this source comes from `c`.
3342        assert_eq!(source.source_name(), Some(&"foo".parse().unwrap()));
3343        assert_eq!(
3344            source.source_moniker(),
3345            ExtendedMoniker::ComponentInstance("c".parse().unwrap())
3346        );
3347    }
3348
3349    ///   a
3350    ///  / \
3351    /// b   c
3352    ///
3353    /// a: offer to b with a service instance renamed from child c
3354    /// b: use from parent
3355    /// c: expose from self
3356    pub async fn test_route_renamed_service_instance_from_sibling(&self) {
3357        let use_decl = UseBuilder::service().name("foo").build();
3358        let components = vec![
3359            (
3360                "a",
3361                ComponentDeclBuilder::new()
3362                    .offer(
3363                        OfferBuilder::service()
3364                            .name("foo")
3365                            .source_static_child("c")
3366                            .target_static_child("b")
3367                            .renamed_instances([("instance_0", "renamed_instance_0")]),
3368                    )
3369                    .child_default("b")
3370                    .child_default("c")
3371                    .build(),
3372            ),
3373            ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3374            (
3375                "c",
3376                ComponentDeclBuilder::new()
3377                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3378                    .service_default("foo")
3379                    .build(),
3380            ),
3381        ];
3382        let model = T::new("a", components).build().await;
3383        let b_component =
3384            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3385        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3386        let source = debug_route_sandbox_path(&b_component, &UseDecl::Service(use_decl))
3387            .await
3388            .expect("failed to route service");
3389
3390        // Verify this source comes from `c`.
3391        assert_eq!(source.source_name(), Some(&"foo".parse().unwrap()));
3392        assert_eq!(
3393            source.source_moniker(),
3394            ExtendedMoniker::ComponentInstance("c".parse().unwrap())
3395        );
3396    }
3397
3398    ///  a
3399    ///   \
3400    ///    b
3401    ///
3402    /// a: declares runner "elf" with service format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from "self".
3403    /// a: registers runner "elf" from self in environment as "hobbit".
3404    /// b: uses runner "hobbit".
3405    pub async fn test_route_runner_from_parent_environment(&self) {
3406        let components = vec![
3407            (
3408                "a",
3409                ComponentDeclBuilder::new()
3410                    .child(ChildBuilder::new().name("b").environment("env"))
3411                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3412                        source_name: "elf".parse().unwrap(),
3413                        source: RegistrationSource::Self_,
3414                        target_name: "hobbit".parse().unwrap(),
3415                    }))
3416                    .runner_default("elf")
3417                    .build(),
3418            ),
3419            ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3420        ];
3421
3422        let model = T::new("a", components).build().await;
3423        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3424        let b_component =
3425            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3426        let source = debug_route_sandbox_path(
3427            &b_component,
3428            &UseDecl::Runner(UseRunnerDecl {
3429                source: UseSource::Environment,
3430                source_name: "hobbit".parse().unwrap(),
3431                source_dictionary: Default::default(),
3432            }),
3433        )
3434        .await
3435        .expect("failed to route runner");
3436
3437        // Verify this source comes from `a`.
3438        match source {
3439            CapabilitySource::Component(ComponentSource {
3440                capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3441                moniker,
3442            }) => {
3443                assert_eq!(name, "elf");
3444                assert_eq!(
3445                    source_path.expect("missing source path"),
3446                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3447                        .parse::<cm_types::Path>()
3448                        .unwrap()
3449                );
3450                assert_eq!(&moniker, a_component.moniker());
3451            }
3452            _ => panic!("bad capability source"),
3453        };
3454    }
3455
3456    ///   a
3457    ///    \
3458    ///     b
3459    ///      \
3460    ///       c
3461    ///
3462    /// a: declares runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self.
3463    /// a: offers runner "elf" from self to "b" as "dwarf".
3464    /// b: registers runner "dwarf" from realm in environment as "hobbit".
3465    /// c: uses runner "hobbit".
3466    pub async fn test_route_runner_from_grandparent_environment(&self) {
3467        let components = vec![
3468            (
3469                "a",
3470                ComponentDeclBuilder::new()
3471                    .child_default("b")
3472                    .offer(
3473                        OfferBuilder::runner()
3474                            .name("elf")
3475                            .target_name("dwarf")
3476                            .source(OfferSource::Self_)
3477                            .target_static_child("b"),
3478                    )
3479                    .runner_default("elf")
3480                    .build(),
3481            ),
3482            (
3483                "b",
3484                ComponentDeclBuilder::new()
3485                    .child(ChildBuilder::new().name("c").environment("env"))
3486                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3487                        source_name: "dwarf".parse().unwrap(),
3488                        source: RegistrationSource::Parent,
3489                        target_name: "hobbit".parse().unwrap(),
3490                    }))
3491                    .build(),
3492            ),
3493            ("c", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3494        ];
3495
3496        let model = T::new("a", components).build().await;
3497        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3498        let c_component =
3499            model.look_up_instance(&["b", "c"].try_into().unwrap()).await.expect("c instance");
3500        let source = debug_route_sandbox_path(
3501            &c_component,
3502            &UseDecl::Runner(UseRunnerDecl {
3503                source: UseSource::Environment,
3504                source_name: "hobbit".parse().unwrap(),
3505                source_dictionary: Default::default(),
3506            }),
3507        )
3508        .await
3509        .expect("failed to route runner");
3510
3511        // Verify this source comes from `a`.
3512        match source {
3513            CapabilitySource::Component(ComponentSource {
3514                capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3515                moniker,
3516            }) => {
3517                assert_eq!(name, "elf");
3518                assert_eq!(
3519                    source_path.expect("missing source path"),
3520                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3521                        .parse::<cm_types::Path>()
3522                        .unwrap()
3523                );
3524                assert_eq!(&moniker, a_component.moniker());
3525            }
3526            _ => panic!("bad capability source"),
3527        };
3528    }
3529
3530    ///   a
3531    ///  / \
3532    /// b   c
3533    ///
3534    /// a: registers runner "dwarf" from "b" in environment as "hobbit".
3535    /// b: exposes runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self as "dwarf".
3536    /// c: uses runner "hobbit".
3537    pub async fn test_route_runner_from_sibling_environment(&self) {
3538        let components = vec![
3539            (
3540                "a",
3541                ComponentDeclBuilder::new()
3542                    .child_default("b")
3543                    .child(ChildBuilder::new().name("c").environment("env"))
3544                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3545                        source_name: "dwarf".parse().unwrap(),
3546                        source: RegistrationSource::Child("b".parse().unwrap()),
3547                        target_name: "hobbit".parse().unwrap(),
3548                    }))
3549                    .build(),
3550            ),
3551            (
3552                "b",
3553                ComponentDeclBuilder::new()
3554                    .expose(
3555                        ExposeBuilder::runner()
3556                            .name("elf")
3557                            .target_name("dwarf")
3558                            .source(ExposeSource::Self_),
3559                    )
3560                    .runner_default("elf")
3561                    .build(),
3562            ),
3563            ("c", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3564        ];
3565
3566        let model = T::new("a", components).build().await;
3567        let b_component =
3568            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3569        let c_component =
3570            model.look_up_instance(&["c"].try_into().unwrap()).await.expect("c instance");
3571        let source = debug_route_sandbox_path(
3572            &c_component,
3573            &UseDecl::Runner(UseRunnerDecl {
3574                source: UseSource::Environment,
3575                source_name: "hobbit".parse().unwrap(),
3576                source_dictionary: Default::default(),
3577            }),
3578        )
3579        .await
3580        .expect("failed to route runner");
3581
3582        // Verify this source comes from `b`.
3583        match source {
3584            CapabilitySource::Component(ComponentSource {
3585                capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3586                moniker,
3587            }) => {
3588                assert_eq!(name, "elf");
3589                assert_eq!(
3590                    source_path.expect("missing source path"),
3591                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3592                        .parse::<cm_types::Path>()
3593                        .unwrap()
3594                );
3595                assert_eq!(&moniker, b_component.moniker());
3596            }
3597            _ => panic!("bad capability source"),
3598        };
3599    }
3600
3601    ///   a
3602    ///    \
3603    ///     b
3604    ///      \
3605    ///       c
3606    ///
3607    /// a: declares runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self.
3608    /// a: registers runner "elf" from realm in environment as "hobbit".
3609    /// b: creates environment extending from realm.
3610    /// c: uses runner "hobbit".
3611    pub async fn test_route_runner_from_inherited_environment(&self) {
3612        let components = vec![
3613            (
3614                "a",
3615                ComponentDeclBuilder::new()
3616                    .child(ChildBuilder::new().name("b").environment("env"))
3617                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3618                        source_name: "elf".parse().unwrap(),
3619                        source: RegistrationSource::Self_,
3620                        target_name: "hobbit".parse().unwrap(),
3621                    }))
3622                    .runner_default("elf")
3623                    .build(),
3624            ),
3625            (
3626                "b",
3627                ComponentDeclBuilder::new()
3628                    .child(ChildBuilder::new().name("c").environment("env"))
3629                    .environment(EnvironmentBuilder::new().name("env"))
3630                    .build(),
3631            ),
3632            ("c", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3633        ];
3634
3635        let model = T::new("a", components).build().await;
3636        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3637        let c_component =
3638            model.look_up_instance(&["b", "c"].try_into().unwrap()).await.expect("c instance");
3639        let source = debug_route_sandbox_path(
3640            &c_component,
3641            &UseDecl::Runner(UseRunnerDecl {
3642                source: UseSource::Environment,
3643                source_name: "hobbit".parse().unwrap(),
3644                source_dictionary: Default::default(),
3645            }),
3646        )
3647        .await
3648        .expect("failed to route runner");
3649
3650        // Verify this source comes from `a`.
3651        match source {
3652            CapabilitySource::Component(ComponentSource {
3653                capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3654                moniker,
3655            }) => {
3656                assert_eq!(name, "elf");
3657                assert_eq!(
3658                    source_path.expect("missing source path"),
3659                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3660                        .parse::<cm_types::Path>()
3661                        .unwrap()
3662                );
3663                assert_eq!(&moniker, a_component.moniker());
3664            }
3665            _ => panic!("bad capability source"),
3666        };
3667    }
3668
3669    ///  a
3670    ///   \
3671    ///    b
3672    ///
3673    /// a: declares runner "elf" with service format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from "self".
3674    /// a: registers runner "elf" from self in environment as "hobbit".
3675    /// b: uses runner "hobbit". Fails because "hobbit" was not in environment.
3676    pub async fn test_route_runner_from_environment_not_found(&self) {
3677        let components = vec![
3678            (
3679                "a",
3680                ComponentDeclBuilder::new()
3681                    .child(ChildBuilder::new().name("b").environment("env"))
3682                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3683                        source_name: "elf".parse().unwrap(),
3684                        source: RegistrationSource::Self_,
3685                        target_name: "dwarf".parse().unwrap(),
3686                    }))
3687                    .runner_default("elf")
3688                    .build(),
3689            ),
3690            ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3691        ];
3692
3693        let model = T::new("a", components).build().await;
3694        let b_component =
3695            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3696        let route_result = debug_route_sandbox_path(
3697            &b_component,
3698            &UseDecl::Runner(UseRunnerDecl {
3699                source: UseSource::Environment,
3700                source_name: "hobbit".parse().unwrap(),
3701                source_dictionary: Default::default(),
3702            }),
3703        )
3704        .await;
3705
3706        assert_matches!(
3707            route_result,
3708            Err(RoutingError::RouteSourceNotFound { moniker, verb, source, capability_type, capability_name, .. })
3709                if &format!("{capability_name}") == "hobbit"
3710                    && moniker == *b_component.moniker()
3711                    && capability_type == CapabilityTypeName::Runner
3712                    && verb == RouteVerb::Use
3713                    && source == GenericRef::Environment
3714        );
3715    }
3716
3717    ///   a
3718    ///    \
3719    ///     b
3720    ///
3721    /// a: registers built-in runner "elf" from realm in environment as "hobbit".
3722    /// b: uses runner "hobbit".
3723    pub async fn test_route_builtin_runner(&self) {
3724        let components = vec![
3725            (
3726                "a",
3727                ComponentDeclBuilder::new()
3728                    .child(ChildBuilder::new().name("b").environment("env"))
3729                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3730                        source_name: "elf".parse().unwrap(),
3731                        source: RegistrationSource::Parent,
3732                        target_name: "hobbit".parse().unwrap(),
3733                    }))
3734                    .build(),
3735            ),
3736            ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3737        ];
3738
3739        let mut builder = T::new("a", components);
3740        builder.set_builtin_capabilities(vec![CapabilityDecl::Runner(RunnerDecl {
3741            name: "elf".parse().unwrap(),
3742            source_path: None,
3743        })]);
3744        builder.register_mock_builtin_runner("elf");
3745        let model = builder.build().await;
3746
3747        let b_component =
3748            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3749        let source = debug_route_sandbox_path(
3750            &b_component,
3751            &UseDecl::Runner(UseRunnerDecl {
3752                source: UseSource::Environment,
3753                source_name: "hobbit".parse().unwrap(),
3754                source_dictionary: Default::default(),
3755            }),
3756        )
3757        .await
3758        .expect("failed to route runner");
3759
3760        // Verify this is a built-in source.
3761        match source {
3762            CapabilitySource::Builtin(BuiltinSource {
3763                capability: InternalCapability::Runner(name),
3764                ..
3765            }) => {
3766                assert_eq!(name, "elf");
3767            }
3768            _ => panic!("bad capability source"),
3769        };
3770    }
3771
3772    ///   a
3773    ///
3774    /// a: uses built-in runner "elf" from the root environment.
3775    pub async fn test_route_builtin_runner_from_root_env(&self) {
3776        let use_runner_decl =
3777            UseBuilder::runner().source(UseSource::Environment).name("elf").build();
3778        let components = vec![(
3779            "a",
3780            ComponentDeclBuilder::new_empty_component().use_(use_runner_decl.clone()).build(),
3781        )];
3782
3783        let mut builder = T::new("a", components);
3784        builder.set_builtin_capabilities(vec![CapabilityDecl::Runner(RunnerDecl {
3785            name: "elf".parse().unwrap(),
3786            source_path: None,
3787        })]);
3788        builder.register_mock_builtin_runner("elf");
3789        let model = builder.build().await;
3790
3791        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3792        let source = debug_route_sandbox_path(&a_component, &use_runner_decl)
3793            .await
3794            .expect("failed to route runner");
3795
3796        // Verify this is a built-in source.
3797        match source {
3798            CapabilitySource::Builtin(BuiltinSource {
3799                capability: InternalCapability::Runner(name),
3800                ..
3801            }) => {
3802                assert_eq!(name, "elf");
3803            }
3804            _ => panic!("bad capability source"),
3805        };
3806    }
3807
3808    ///  a
3809    ///   \
3810    ///    b
3811    ///
3812    /// a: registers built-in runner "elf" from realm in environment as "hobbit". The ELF runner is
3813    ///    registered in the root environment, but not declared as a built-in capability.
3814    /// b: uses runner "hobbit"; should fail.
3815    pub async fn test_route_builtin_runner_not_found(&self) {
3816        let components = vec![
3817            (
3818                "a",
3819                ComponentDeclBuilder::new()
3820                    .child(ChildBuilder::new().name("b").environment("env"))
3821                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3822                        source_name: "elf".parse().unwrap(),
3823                        source: RegistrationSource::Parent,
3824                        target_name: "hobbit".parse().unwrap(),
3825                    }))
3826                    .build(),
3827            ),
3828            ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3829        ];
3830
3831        let mut builder = T::new("a", components);
3832        builder.register_mock_builtin_runner("elf");
3833
3834        let model = builder.build().await;
3835        let b_component =
3836            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3837        let route_result = debug_route_sandbox_path(
3838            &b_component,
3839            &UseDecl::Runner(UseRunnerDecl {
3840                source: UseSource::Environment,
3841                source_name: "hobbit".parse().unwrap(),
3842                source_dictionary: Default::default(),
3843            }),
3844        )
3845        .await;
3846
3847        assert_matches!(
3848            route_result,
3849            Err(RoutingError::RouteSourceNotFound { moniker, verb, source, capability_type, capability_name, .. })
3850                if &format!("{capability_name}") == "elf"
3851                    && moniker == Moniker::root()
3852                    && capability_type == CapabilityTypeName::Runner
3853                    && verb == RouteVerb::Register
3854                    && source == GenericRef::Parent
3855        );
3856    }
3857
3858    ///   a
3859    ///
3860    /// a: Attempts to use unregistered runner "hobbit" from the root environment.
3861    ///    The runner is provided as a built-in capability, but not registered in
3862    ///    the root environment.
3863    pub async fn test_route_builtin_runner_from_root_env_registration_not_found(&self) {
3864        let use_runner_decl = UseRunnerDecl {
3865            source: UseSource::Environment,
3866            source_name: "hobbit".parse().unwrap(),
3867            source_dictionary: Default::default(),
3868        };
3869        let components = vec![(
3870            "a",
3871            ComponentDeclBuilder::new_empty_component().use_(use_runner_decl.clone()).build(),
3872        )];
3873
3874        let builder = T::new("a", components);
3875        let model = builder.build().await;
3876
3877        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3878        let route_result =
3879            debug_route_sandbox_path(&a_component, &UseDecl::Runner(use_runner_decl)).await;
3880
3881        assert_matches!(
3882            route_result,
3883            Err(RoutingError::RouteSourceNotFound { moniker, verb, source, capability_type, capability_name, .. })
3884                if &format!("{capability_name}") == "hobbit"
3885                    && moniker == *a_component.moniker()
3886                    && capability_type == CapabilityTypeName::Runner
3887                    && verb == RouteVerb::Use
3888                    && source == GenericRef::Environment
3889        );
3890    }
3891
3892    ///  a
3893    ///   \
3894    ///    b
3895    ///
3896    /// a: uses runner "elf" from "#b" as "dwarf".
3897    /// b: exposes runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self as "dwarf".
3898    pub async fn test_use_runner_from_child(&self) {
3899        let components = vec![
3900            (
3901                "a",
3902                ComponentDeclBuilder::new_empty_component()
3903                    .use_(UseBuilder::runner().source_static_child("b").name("dwarf"))
3904                    .child_default("b")
3905                    .build(),
3906            ),
3907            (
3908                "b",
3909                ComponentDeclBuilder::new()
3910                    .expose(
3911                        ExposeBuilder::runner()
3912                            .name("elf")
3913                            .target_name("dwarf")
3914                            .source(ExposeSource::Self_),
3915                    )
3916                    .runner_default("elf")
3917                    .build(),
3918            ),
3919        ];
3920
3921        let model = T::new("a", components).build().await;
3922        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3923        let b_component =
3924            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3925        let source = debug_route_sandbox_path(
3926            &a_component,
3927            &UseDecl::Runner(UseRunnerDecl {
3928                source: UseSource::Child("b".parse().unwrap()),
3929                source_name: "dwarf".parse().unwrap(),
3930                source_dictionary: Default::default(),
3931            }),
3932        )
3933        .await
3934        .expect("failed to route runner");
3935
3936        // Verify this source comes from `b`.
3937        match source {
3938            CapabilitySource::Component(ComponentSource {
3939                capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3940                moniker,
3941            }) => {
3942                assert_eq!(name, "elf");
3943                assert_eq!(
3944                    source_path.expect("missing source path"),
3945                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3946                        .parse::<cm_types::Path>()
3947                        .unwrap()
3948                );
3949                assert_eq!(&moniker, b_component.moniker());
3950            }
3951            _ => panic!("bad capability source"),
3952        };
3953    }
3954
3955    ///  a
3956    ///   \
3957    ///    b
3958    ///
3959    /// a: offers runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self as "dwarf".
3960    /// b: uses runner "elf" from "parent" as "dwarf".
3961    pub async fn test_use_runner_from_parent(&self) {
3962        let components = vec![
3963            (
3964                "a",
3965                ComponentDeclBuilder::new()
3966                    .offer(
3967                        OfferBuilder::runner()
3968                            .name("elf")
3969                            .target_name("dwarf")
3970                            .source(OfferSource::Self_)
3971                            .target_static_child("b"),
3972                    )
3973                    .runner_default("elf")
3974                    .child_default("b")
3975                    .build(),
3976            ),
3977            (
3978                "b",
3979                ComponentDeclBuilder::new_empty_component()
3980                    .use_(UseBuilder::runner().name("dwarf"))
3981                    .build(),
3982            ),
3983        ];
3984
3985        let model = T::new("a", components).build().await;
3986        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3987        let b_component =
3988            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3989        let source = debug_route_sandbox_path(
3990            &b_component,
3991            &UseDecl::Runner(UseRunnerDecl {
3992                source: UseSource::Parent,
3993                source_name: "dwarf".parse().unwrap(),
3994                source_dictionary: Default::default(),
3995            }),
3996        )
3997        .await
3998        .expect("failed to route runner");
3999
4000        // Verify this source comes from `a`.
4001        match source {
4002            CapabilitySource::Component(ComponentSource {
4003                capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
4004                moniker,
4005            }) => {
4006                assert_eq!(name, "elf");
4007                assert_eq!(
4008                    source_path.expect("missing source path"),
4009                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
4010                        .parse::<cm_types::Path>()
4011                        .unwrap()
4012                );
4013                assert_eq!(&moniker, a_component.moniker());
4014            }
4015            _ => panic!("bad capability source"),
4016        };
4017    }
4018
4019    ///  a
4020    ///   \
4021    ///    b
4022    ///
4023    /// a: declares runner "elf" with service format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from "self".
4024    /// a: registers runner "elf" from self in environment as "hobbit".
4025    /// b: uses runner "hobbit" from environment.
4026    pub async fn test_use_runner_from_parent_environment(&self) {
4027        let components = vec![
4028            (
4029                "a",
4030                ComponentDeclBuilder::new()
4031                    .child(ChildBuilder::new().name("b").environment("env"))
4032                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
4033                        source_name: "elf".parse().unwrap(),
4034                        source: RegistrationSource::Self_,
4035                        target_name: "hobbit".parse().unwrap(),
4036                    }))
4037                    .runner_default("elf")
4038                    .build(),
4039            ),
4040            (
4041                "b",
4042                ComponentDeclBuilder::new_empty_component()
4043                    .use_(UseBuilder::runner().source(UseSource::Environment).name("hobbit"))
4044                    .build(),
4045            ),
4046        ];
4047
4048        let model = T::new("a", components).build().await;
4049        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
4050        let b_component =
4051            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
4052        let source = debug_route_sandbox_path(
4053            &b_component,
4054            &UseDecl::Runner(UseRunnerDecl {
4055                source: UseSource::Environment,
4056                source_name: "hobbit".parse().unwrap(),
4057                source_dictionary: Default::default(),
4058            }),
4059        )
4060        .await
4061        .expect("failed to route runner");
4062
4063        // Verify this source comes from `a`.
4064        match source {
4065            CapabilitySource::Component(ComponentSource {
4066                capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
4067                moniker,
4068            }) => {
4069                assert_eq!(name, "elf");
4070                assert_eq!(
4071                    source_path.expect("missing source path"),
4072                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
4073                        .parse::<cm_types::Path>()
4074                        .unwrap()
4075                );
4076                assert_eq!(&moniker, a_component.moniker());
4077            }
4078            _ => panic!("bad capability source"),
4079        };
4080    }
4081
4082    ///  a
4083    ///   \
4084    ///    b
4085    ///
4086    /// b: declares "fuchsia.MyConfig" capability
4087    /// b: uses "fuchsia.MyConfig" from self
4088    pub async fn test_use_config_from_self(&self) {
4089        let good_value = ConfigSingleValue::Int8(12);
4090        let use_config = UseBuilder::config()
4091            .source(UseSource::Self_)
4092            .name("fuchsia.MyConfig")
4093            .target_name("my_config")
4094            .config_type(ConfigValueType::Int8)
4095            .build();
4096        let components = vec![
4097            ("a", ComponentDeclBuilder::new().child_default("b").build()),
4098            (
4099                "b",
4100                ComponentDeclBuilder::new()
4101                    .capability(
4102                        CapabilityBuilder::config()
4103                            .name("fuchsia.MyConfig")
4104                            .value(good_value.clone().into()),
4105                    )
4106                    .use_(use_config.clone())
4107                    .config(ConfigDecl {
4108                        fields: Box::from([ConfigField {
4109                            key: "my_config".into(),
4110                            type_: ConfigValueType::Int8,
4111                            mutability: Default::default(),
4112                        }]),
4113                        checksum: ConfigChecksum::Sha256([0; 32]),
4114                        value_source: ConfigValueSource::Capabilities(Default::default()),
4115                    })
4116                    .build(),
4117            ),
4118        ];
4119
4120        let model = T::new("a", components).build().await;
4121        let child_component = model.look_up_instance(&["b"].try_into().unwrap()).await.unwrap();
4122
4123        let UseDecl::Config(use_config) = use_config else { panic!() };
4124        let value =
4125            routing::config::route_config_value(&use_config, &child_component).await.unwrap();
4126        assert_eq!(value, Some(ConfigValue::Single(good_value)));
4127    }
4128
4129    ///  a
4130    ///   \
4131    ///    b
4132    ///
4133    /// a: declares "fuchsia.MyConfig" capability
4134    /// b: uses "fuchsia.MyConfig" from parent
4135    pub async fn test_use_config_from_parent(&self) {
4136        let good_value = ConfigSingleValue::Int8(12);
4137        let use_config = UseBuilder::config()
4138            .source(UseSource::Parent)
4139            .name("fuchsia.MyConfig")
4140            .target_name("my_config")
4141            .config_type(ConfigValueType::Int8)
4142            .build();
4143        let components = vec![
4144            (
4145                "a",
4146                ComponentDeclBuilder::new()
4147                    .capability(
4148                        CapabilityBuilder::config()
4149                            .name("fuchsia.MyConfig")
4150                            .value(good_value.clone().into()),
4151                    )
4152                    .offer(
4153                        OfferBuilder::config()
4154                            .name("fuchsia.MyConfig")
4155                            .source(OfferSource::Self_)
4156                            .target(OfferTarget::Child(ChildRef {
4157                                name: "b".parse().unwrap(),
4158                                collection: None,
4159                            })),
4160                    )
4161                    .child_default("b")
4162                    .build(),
4163            ),
4164            (
4165                "b",
4166                ComponentDeclBuilder::new()
4167                    .use_(use_config.clone())
4168                    .config(ConfigDecl {
4169                        fields: Box::from([ConfigField {
4170                            key: "my_config".into(),
4171                            type_: ConfigValueType::Int8,
4172                            mutability: Default::default(),
4173                        }]),
4174                        checksum: ConfigChecksum::Sha256([0; 32]),
4175                        value_source: ConfigValueSource::Capabilities(Default::default()),
4176                    })
4177                    .build(),
4178            ),
4179        ];
4180
4181        let model = T::new("a", components).build().await;
4182        let child_component = model.look_up_instance(&["b"].try_into().unwrap()).await.unwrap();
4183
4184        let UseDecl::Config(use_config) = use_config else { panic!() };
4185        let value =
4186            routing::config::route_config_value(&use_config, &child_component).await.unwrap();
4187        assert_eq!(value, Some(ConfigValue::Single(good_value)));
4188    }
4189
4190    ///  a
4191    ///   \
4192    ///    b
4193    ///
4194    /// a: routes "fuchsia.MyConfig" from void
4195    /// b: uses "fuchsia.MyConfig" from parent
4196    pub async fn test_use_config_from_void(&self) {
4197        let mut use_config = UseBuilder::config()
4198            .source(UseSource::Parent)
4199            .name("fuchsia.MyConfig")
4200            .target_name("my_config")
4201            .availability(Availability::Optional)
4202            .config_type(ConfigValueType::Int8)
4203            .build();
4204        match &mut use_config {
4205            UseDecl::Config(use_config_decl) => {
4206                use_config_decl.default = Some(ConfigValue::Single(ConfigSingleValue::Int8(0)))
4207            }
4208            _ => panic!("unexpected use declaration variant"),
4209        }
4210        let components = vec![
4211            (
4212                "a",
4213                ComponentDeclBuilder::new()
4214                    .offer(
4215                        OfferBuilder::config()
4216                            .name("fuchsia.MyConfig")
4217                            .source(OfferSource::Void)
4218                            .availability(Availability::Optional)
4219                            .target(OfferTarget::Child(ChildRef {
4220                                name: "b".parse().unwrap(),
4221                                collection: None,
4222                            })),
4223                    )
4224                    .child_default("b")
4225                    .build(),
4226            ),
4227            (
4228                "b",
4229                ComponentDeclBuilder::new()
4230                    .use_(use_config.clone())
4231                    .config(ConfigDecl {
4232                        fields: Box::from([ConfigField {
4233                            key: "my_config".into(),
4234                            type_: ConfigValueType::Int8,
4235                            mutability: Default::default(),
4236                        }]),
4237                        checksum: ConfigChecksum::Sha256([0; 32]),
4238                        value_source: ConfigValueSource::Capabilities(Default::default()),
4239                    })
4240                    .build(),
4241            ),
4242        ];
4243
4244        let model = T::new("a", components).build().await;
4245        let child_component = model.look_up_instance(&["b"].try_into().unwrap()).await.unwrap();
4246
4247        let UseDecl::Config(use_config) = use_config else { panic!() };
4248        let value =
4249            routing::config::route_config_value(&use_config, &child_component).await.unwrap();
4250        assert_eq!(value, Some(ConfigValue::Single(ConfigSingleValue::Int8(0))));
4251    }
4252
4253    pub async fn test_use_dictionary_protocol_from_self(&self) {
4254        let components = vec![(
4255            "root",
4256            ComponentDeclBuilder::new()
4257                .dictionary_default("my_dict")
4258                .use_(
4259                    UseBuilder::protocol()
4260                        .source(UseSource::Self_)
4261                        .name("A")
4262                        .path("/svc/B")
4263                        .from_dictionary("my_dict"),
4264                )
4265                .build(),
4266        )];
4267
4268        let model = T::new("root", components).build().await;
4269        let root_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
4270
4271        let route_result = debug_route_sandbox_path(
4272            &root_component,
4273            &UseDecl::Protocol(UseProtocolDecl {
4274                source: UseSource::Self_,
4275                source_name: "A".parse().unwrap(),
4276                source_dictionary: "my_dict".parse().unwrap(),
4277                target_path: Some("/svc/B".parse().unwrap()),
4278                #[cfg(fuchsia_api_level_at_least = "29")]
4279                numbered_handle: None,
4280                dependency_type: DependencyType::Strong,
4281                availability: Availability::Required,
4282            }),
4283        )
4284        .await;
4285
4286        assert_matches!(
4287            route_result,
4288            Err(RoutingError::RouteSourceNotFound { moniker, verb, source, capability_type, capability_name, .. })
4289                if &format!("{capability_name}") == "my_dict/A"
4290                    && moniker == Moniker::root()
4291                    && capability_type == CapabilityTypeName::Protocol
4292                    && verb == RouteVerb::Use
4293                    && source == GenericRef::Self_
4294        );
4295    }
4296
4297    pub async fn test_offer_dictionary_to_grandchild_not_supported(&self) {
4298        // Using a dictionary in legacy routing isn't supported. Also, when a
4299        // component uses a protocol from its grandparent that passes through a
4300        // dictionary, that should trickle down into a DictionariesNotSupported
4301        // rather than another type of routing error.
4302        let components = vec![
4303            (
4304                "root",
4305                ComponentDeclBuilder::new()
4306                    // It doesn't matter that this dictionary doesn't
4307                    // actually contain the required protocol, since routing
4308                    // will fail before that.
4309                    .dictionary_default("parent_dict")
4310                    .offer(
4311                        OfferBuilder::protocol()
4312                            .name("A")
4313                            .from_dictionary("parent_dict")
4314                            .source(OfferSource::Self_)
4315                            .target_static_child("intermediate"),
4316                    )
4317                    .child_default("intermediate")
4318                    .build(),
4319            ),
4320            (
4321                "intermediate",
4322                ComponentDeclBuilder::new()
4323                    .offer(
4324                        OfferBuilder::protocol()
4325                            .source(OfferSource::Parent)
4326                            .name("A")
4327                            .target_static_child("leaf"),
4328                    )
4329                    .child_default("leaf")
4330                    .build(),
4331            ),
4332            (
4333                "leaf",
4334                ComponentDeclBuilder::new()
4335                    .use_(UseBuilder::protocol().source(UseSource::Parent).name("A"))
4336                    .build(),
4337            ),
4338        ];
4339
4340        let model = T::new("root", components).build().await;
4341        let leaf_component = model
4342            .look_up_instance(&["intermediate", "leaf"].try_into().unwrap())
4343            .await
4344            .expect("leaf instance");
4345
4346        let route_result = debug_route_sandbox_path(
4347            &leaf_component,
4348            &UseDecl::Protocol(UseProtocolDecl {
4349                source: UseSource::Parent,
4350                source_name: "A".parse().unwrap(),
4351                source_dictionary: Default::default(),
4352                target_path: Some("/svc/dict_protocol".parse().unwrap()),
4353                #[cfg(fuchsia_api_level_at_least = "29")]
4354                numbered_handle: None,
4355                dependency_type: DependencyType::Strong,
4356                availability: Availability::Required,
4357            }),
4358        )
4359        .await;
4360
4361        assert_matches!(
4362            route_result,
4363            Err(RoutingError::BedrockNotPresentInDictionary { name, .. })
4364                if name == "program_input/namespace/svc/dict_protocol"
4365        );
4366    }
4367
4368    pub async fn test_expose_dictionary_to_grandparent_not_supported(&self) {
4369        // Same as above: using a dictionary in legacy routing isn't supported,
4370        // but check the expose direction.
4371        let components = vec![
4372            (
4373                "root",
4374                ComponentDeclBuilder::new()
4375                    .use_(
4376                        UseBuilder::protocol()
4377                            .source(UseSource::Child("intermediate".parse().unwrap()))
4378                            .name("A"),
4379                    )
4380                    .child_default("intermediate")
4381                    .build(),
4382            ),
4383            (
4384                "intermediate",
4385                ComponentDeclBuilder::new()
4386                    .expose(
4387                        ExposeBuilder::protocol()
4388                            .source(ExposeSource::Child("leaf".parse().unwrap()))
4389                            .name("A")
4390                            .target(ExposeTarget::Parent),
4391                    )
4392                    .child_default("leaf")
4393                    .build(),
4394            ),
4395            (
4396                "leaf",
4397                ComponentDeclBuilder::new()
4398                    .dictionary_default("child_dict")
4399                    .expose(
4400                        ExposeBuilder::protocol()
4401                            .name("A")
4402                            .from_dictionary("child_dict")
4403                            .source(ExposeSource::Self_)
4404                            .target(ExposeTarget::Parent),
4405                    )
4406                    .build(),
4407            ),
4408        ];
4409
4410        let model = T::new("root", components).build().await;
4411        let root_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
4412        let route_result = debug_route_sandbox_path(
4413            &root_component,
4414            &UseDecl::Protocol(UseProtocolDecl {
4415                source: UseSource::Child("intermediate".parse().unwrap()),
4416                source_name: "A".parse().unwrap(),
4417                source_dictionary: Default::default(),
4418                target_path: Some("/svc/dict_protocol".parse().unwrap()),
4419                #[cfg(fuchsia_api_level_at_least = "29")]
4420                numbered_handle: None,
4421                dependency_type: DependencyType::Strong,
4422                availability: Availability::Required,
4423            }),
4424        )
4425        .await;
4426
4427        assert_matches!(
4428            route_result,
4429            Err(RoutingError::BedrockNotPresentInDictionary { name, .. })
4430                if name == "program_input/namespace/svc/dict_protocol"
4431        );
4432    }
4433}