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