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 moniker::{ExtendedMoniker, Moniker};
26use routing::capability_source::{
27    AggregateCapability, AggregateMember, AnonymizedAggregateSource, BuiltinSource,
28    CapabilitySource, ComponentCapability, ComponentSource, FilteredAggregateProviderSource,
29    InternalCapability,
30};
31use routing::component_instance::ComponentInstanceInterface;
32use routing::error::RoutingError;
33use routing::mapper::NoopRouteMapper;
34use routing::{RouteRequest, RouteSource, route_capability};
35use std::collections::HashSet;
36use std::marker::PhantomData;
37use std::path::{Path, PathBuf};
38use std::sync::Arc;
39use {
40    fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_runner as fcrunner,
41    fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio, zx_status as zx,
42};
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: Vec<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        let ExposeDecl::Protocol(expose_decl) = expose_decl else {
1660            unreachable!();
1661        };
1662        assert_matches!(
1663        route_capability(RouteRequest::ExposeProtocol(expose_decl), &root_instance, &mut NoopRouteMapper).await,
1664            Ok(RouteSource {
1665                source: CapabilitySource::Component(ComponentSource {
1666                        capability: ComponentCapability::Protocol(capability_decl),
1667                        moniker,
1668                    }),
1669                relative_path,
1670            }) if capability_decl == expected_protocol_decl && moniker == expected_source_moniker && relative_path.is_dot()
1671        );
1672    }
1673
1674    ///   a
1675    ///  / \
1676    /// b   c
1677    ///
1678    /// b: exposes directory /data/foo from self as /data/bar to framework (NOT realm)
1679    /// a: offers directory /data/bar from b as /data/baz to c, but it is not exposed via realm
1680    /// c: uses /data/baz as /data/hippo
1681    pub async fn test_use_from_expose_to_framework(&self) {
1682        let components = vec![
1683            (
1684                "a",
1685                ComponentDeclBuilder::new()
1686                    .offer(
1687                        OfferBuilder::directory()
1688                            .name("bar_data")
1689                            .target_name("baz_data")
1690                            .source_static_child("b")
1691                            .target_static_child("c")
1692                            .rights(fio::R_STAR_DIR),
1693                    )
1694                    .offer(
1695                        OfferBuilder::protocol()
1696                            .name("bar")
1697                            .target_name("baz")
1698                            .source_static_child("b")
1699                            .target_static_child("c"),
1700                    )
1701                    .child_default("b")
1702                    .child_default("c")
1703                    .build(),
1704            ),
1705            (
1706                "b",
1707                ComponentDeclBuilder::new()
1708                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
1709                    .protocol_default("foo")
1710                    .expose(
1711                        ExposeBuilder::directory()
1712                            .name("foo_data")
1713                            .source(ExposeSource::Self_)
1714                            .target_name("bar_data")
1715                            .target(ExposeTarget::Framework)
1716                            .rights(fio::R_STAR_DIR),
1717                    )
1718                    .expose(
1719                        ExposeBuilder::protocol()
1720                            .name("foo")
1721                            .target_name("bar")
1722                            .source(ExposeSource::Self_)
1723                            .target(ExposeTarget::Framework),
1724                    )
1725                    .build(),
1726            ),
1727            (
1728                "c",
1729                ComponentDeclBuilder::new()
1730                    .use_(UseBuilder::directory().name("baz_data").path("/data/hippo"))
1731                    .use_(UseBuilder::protocol().name("baz").path("/svc/hippo"))
1732                    .build(),
1733            ),
1734        ];
1735        let model = T::new("a", components).build().await;
1736        model
1737            .check_use(
1738                ["c"].try_into().unwrap(),
1739                CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1740            )
1741            .await;
1742        model
1743            .check_use(
1744                ["c"].try_into().unwrap(),
1745                CheckUse::Protocol {
1746                    path: default_service_capability(),
1747                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1748                },
1749            )
1750            .await;
1751    }
1752
1753    ///   a
1754    ///    \
1755    ///     b
1756    ///
1757    /// a: offers directory /data/hippo to b, but a is not executable
1758    /// a: offers service /svc/hippo to b, but a is not executable
1759    /// b: uses directory /data/hippo as /data/hippo, but it's not in its realm
1760    /// b: uses service /svc/hippo as /svc/hippo, but it's not in its realm
1761    pub async fn test_offer_from_non_executable(&self) {
1762        let components = vec![
1763            (
1764                "a",
1765                ComponentDeclBuilder::new_empty_component()
1766                    .capability(CapabilityBuilder::directory().name("hippo_data").path("/data"))
1767                    .protocol_default("hippo")
1768                    .offer(
1769                        OfferBuilder::directory()
1770                            .name("hippo_data")
1771                            .source(OfferSource::Self_)
1772                            .target_static_child("b")
1773                            .rights(fio::R_STAR_DIR),
1774                    )
1775                    .offer(
1776                        OfferBuilder::protocol()
1777                            .name("hippo")
1778                            .source(OfferSource::Self_)
1779                            .target_static_child("b"),
1780                    )
1781                    .child_default("b")
1782                    .build(),
1783            ),
1784            (
1785                "b",
1786                ComponentDeclBuilder::new()
1787                    .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1788                    .use_(UseBuilder::protocol().name("hippo"))
1789                    .build(),
1790            ),
1791        ];
1792        let model = T::new("a", components).build().await;
1793        model
1794            .check_use(
1795                ["b"].try_into().unwrap(),
1796                CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1797            )
1798            .await;
1799        model
1800            .check_use(
1801                ["b"].try_into().unwrap(),
1802                CheckUse::Protocol {
1803                    path: default_service_capability(),
1804                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1805                },
1806            )
1807            .await;
1808    }
1809
1810    ///   a
1811    /// / | \
1812    /// b c d
1813    ///
1814    /// a: offers "foo" from both b and c to d
1815    /// b: exposes "foo" to parent from self
1816    /// c: exposes "foo" to parent from self
1817    /// d: uses "foo" from parent
1818    /// routing an aggregate service with non-conflicting filters should succeed.
1819    pub async fn test_route_filtered_aggregate_service(&self) {
1820        let expected_service_decl = CapabilityBuilder::service().name("foo").build();
1821        let components = vec![
1822            (
1823                "a",
1824                ComponentDeclBuilder::new()
1825                    .offer(
1826                        OfferBuilder::service()
1827                            .name("foo")
1828                            .source_static_child("b")
1829                            .target_static_child("d")
1830                            .source_instance_filter(["instance_0", "instance_1"]),
1831                    )
1832                    .offer(
1833                        OfferBuilder::service()
1834                            .name("foo")
1835                            .source_static_child("c")
1836                            .target_static_child("d")
1837                            .source_instance_filter(["instance_2", "instance_3"]),
1838                    )
1839                    .child_default("b")
1840                    .child_default("c")
1841                    .child_default("d")
1842                    .build(),
1843            ),
1844            (
1845                "b",
1846                ComponentDeclBuilder::new()
1847                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
1848                    .capability(expected_service_decl.clone())
1849                    .build(),
1850            ),
1851            (
1852                "c",
1853                ComponentDeclBuilder::new()
1854                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
1855                    .capability(expected_service_decl.clone())
1856                    .build(),
1857            ),
1858            ("d", ComponentDeclBuilder::new().use_(UseBuilder::service().name("foo")).build()),
1859        ];
1860        let model = T::new("a", components).build().await;
1861
1862        let d_component =
1863            model.look_up_instance(&["d"].try_into().unwrap()).await.expect("b instance");
1864
1865        let source = route_capability(
1866            RouteRequest::UseService(UseServiceDecl {
1867                source: UseSource::Parent,
1868                source_name: "foo".parse().unwrap(),
1869                source_dictionary: Default::default(),
1870                target_path: "/svc/foo".parse().unwrap(),
1871                dependency_type: DependencyType::Strong,
1872                availability: Availability::Required,
1873            }),
1874            &d_component,
1875            &mut NoopRouteMapper,
1876        )
1877        .await
1878        .expect("failed to route service");
1879        match source {
1880            RouteSource {
1881                source:
1882                    CapabilitySource::FilteredAggregateProvider(FilteredAggregateProviderSource {
1883                        capability: AggregateCapability::Service(name),
1884                        ..
1885                    }),
1886                relative_path,
1887            } if relative_path.is_dot() => {
1888                assert_eq!(name, "foo");
1889            }
1890            _ => panic!("bad capability source"),
1891        };
1892    }
1893
1894    ///   a
1895    ///   |
1896    ///   b
1897    ///  /|
1898    /// c d
1899    ///
1900    /// a: offers "foo" from self to `b`
1901    /// b: offers "foo" from parent, c, and itself to d, forming an aggregate
1902    /// c: exposes "foo" to parent from self
1903    /// d: uses "foo" from parent
1904    /// routing an aggregate service without specifying a source_instance_filter should fail.
1905    pub async fn test_route_anonymized_aggregate_service(&self) {
1906        let expected_service_decl = CapabilityBuilder::service().name("foo").build();
1907        let components = vec![
1908            (
1909                "a",
1910                ComponentDeclBuilder::new()
1911                    .offer(
1912                        OfferBuilder::service()
1913                            .name("foo")
1914                            .source(OfferSource::Self_)
1915                            .target_static_child("b"),
1916                    )
1917                    .capability(expected_service_decl.clone())
1918                    .child_default("b")
1919                    .build(),
1920            ),
1921            (
1922                "b",
1923                ComponentDeclBuilder::new()
1924                    .offer(
1925                        OfferBuilder::service()
1926                            .name("foo")
1927                            .source_static_child("c")
1928                            .target_static_child("d"),
1929                    )
1930                    .offer(
1931                        OfferBuilder::service()
1932                            .name("foo")
1933                            .source(OfferSource::Parent)
1934                            .target_static_child("d"),
1935                    )
1936                    .offer(
1937                        OfferBuilder::service()
1938                            .name("foo")
1939                            .source(OfferSource::Self_)
1940                            .target_static_child("d"),
1941                    )
1942                    .capability(expected_service_decl.clone())
1943                    .child_default("c")
1944                    .child_default("d")
1945                    .build(),
1946            ),
1947            (
1948                "c",
1949                ComponentDeclBuilder::new()
1950                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
1951                    .capability(expected_service_decl.clone())
1952                    .build(),
1953            ),
1954            ("d", ComponentDeclBuilder::new().use_(UseBuilder::service().name("foo")).build()),
1955        ];
1956        let test = T::new("a", components).build().await;
1957
1958        let d_component = test.look_up_instance(&"b/d".parse().unwrap()).await.expect("b instance");
1959        let source = route_capability(
1960            RouteRequest::UseService(UseServiceDecl {
1961                source: UseSource::Parent,
1962                source_name: "foo".parse().unwrap(),
1963                source_dictionary: Default::default(),
1964                target_path: "/svc/foo".parse().unwrap(),
1965                dependency_type: DependencyType::Strong,
1966                availability: Availability::Required,
1967            }),
1968            &d_component,
1969            &mut NoopRouteMapper,
1970        )
1971        .await
1972        .unwrap();
1973        match source {
1974            RouteSource {
1975                source:
1976                    CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
1977                        capability: AggregateCapability::Service(name),
1978                        members,
1979                        ..
1980                    }),
1981                relative_path,
1982            } if relative_path.is_dot() => {
1983                assert_eq!(name, "foo");
1984                assert_eq!(members.len(), 3);
1985                for c in [
1986                    AggregateMember::Child(ChildRef {
1987                        name: "c".parse().unwrap(),
1988                        collection: None,
1989                    }),
1990                    AggregateMember::Parent,
1991                    AggregateMember::Self_,
1992                ] {
1993                    assert!(members.contains(&c));
1994                }
1995            }
1996            _ => panic!("bad capability source"),
1997        }
1998    }
1999
2000    ///   a
2001    ///    \
2002    ///     b
2003    ///      \
2004    ///       c
2005    ///
2006    /// a: offers directory /data/foo from self with subdir 's1/s2'
2007    /// b: offers directory /data/foo from realm with subdir 's3'
2008    /// c: uses /data/foo as /data/hippo
2009    pub async fn test_use_directory_with_subdir_from_grandparent(&self) {
2010        let components = vec![
2011            (
2012                "a",
2013                ComponentDeclBuilder::new()
2014                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2015                    .protocol_default("foo")
2016                    .offer(
2017                        OfferBuilder::directory()
2018                            .name("foo_data")
2019                            .source(OfferSource::Self_)
2020                            .target_static_child("b")
2021                            .rights(fio::R_STAR_DIR)
2022                            .subdir("s1/s2"),
2023                    )
2024                    .child_default("b")
2025                    .build(),
2026            ),
2027            (
2028                "b",
2029                ComponentDeclBuilder::new()
2030                    .offer(
2031                        OfferBuilder::directory()
2032                            .name("foo_data")
2033                            .source(OfferSource::Parent)
2034                            .target_static_child("c")
2035                            .rights(fio::R_STAR_DIR)
2036                            .subdir("s3"),
2037                    )
2038                    .child_default("c")
2039                    .build(),
2040            ),
2041            (
2042                "c",
2043                ComponentDeclBuilder::new()
2044                    .use_(UseBuilder::directory().name("foo_data").path("/data/hippo").subdir("s4"))
2045                    .build(),
2046            ),
2047        ];
2048        let model = T::new("a", components).build().await;
2049        model
2050            .create_static_file(Path::new("foo/s1/s2/s3/s4/inner"), "hello")
2051            .await
2052            .expect("failed to create file");
2053        model
2054            .check_use(
2055                ["b", "c"].try_into().unwrap(),
2056                CheckUse::Directory {
2057                    path: default_directory_capability(),
2058                    file: PathBuf::from("inner"),
2059                    expected_res: ExpectedResult::Ok,
2060                },
2061            )
2062            .await;
2063    }
2064
2065    ///   a
2066    ///  / \
2067    /// b   c
2068    ///
2069    ///
2070    /// b: exposes directory /data/foo from self with subdir 's1/s2'
2071    /// a: offers directory /data/foo from `b` to `c` with subdir 's3'
2072    /// c: uses /data/foo as /data/hippo
2073    pub async fn test_use_directory_with_subdir_from_sibling(&self) {
2074        let components = vec![
2075            (
2076                "a",
2077                ComponentDeclBuilder::new()
2078                    .offer(
2079                        OfferBuilder::directory()
2080                            .name("foo_data")
2081                            .source_static_child("b")
2082                            .target_static_child("c")
2083                            .rights(fio::R_STAR_DIR)
2084                            .subdir("s3"),
2085                    )
2086                    .child_default("b")
2087                    .child_default("c")
2088                    .build(),
2089            ),
2090            (
2091                "b",
2092                ComponentDeclBuilder::new()
2093                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2094                    .expose(
2095                        ExposeBuilder::directory()
2096                            .name("foo_data")
2097                            .source(ExposeSource::Self_)
2098                            .rights(fio::R_STAR_DIR)
2099                            .subdir("s1/s2"),
2100                    )
2101                    .build(),
2102            ),
2103            (
2104                "c",
2105                ComponentDeclBuilder::new()
2106                    .use_(UseBuilder::directory().name("foo_data").path("/data/hippo"))
2107                    .build(),
2108            ),
2109        ];
2110        let model = T::new("a", components).build().await;
2111        model
2112            .create_static_file(Path::new("foo/s1/s2/s3/inner"), "hello")
2113            .await
2114            .expect("failed to create file");
2115        model
2116            .check_use(
2117                ["c"].try_into().unwrap(),
2118                CheckUse::Directory {
2119                    path: default_directory_capability(),
2120                    file: PathBuf::from("inner"),
2121                    expected_res: ExpectedResult::Ok,
2122                },
2123            )
2124            .await;
2125    }
2126
2127    ///   a
2128    ///    \
2129    ///     b
2130    ///      \
2131    ///       c
2132    ///
2133    /// c: exposes /data/foo from self
2134    /// b: exposes /data/foo from `c` with subdir `s1/s2`
2135    /// a: exposes /data/foo from `b` with subdir `s3` as /data/hippo
2136    /// use /data/hippo from a's exposed dir
2137    pub async fn test_expose_directory_with_subdir(&self) {
2138        let components = vec![
2139            (
2140                "a",
2141                ComponentDeclBuilder::new()
2142                    .expose(
2143                        ExposeBuilder::directory()
2144                            .name("foo_data")
2145                            .source_static_child("b")
2146                            .target_name("hippo_data")
2147                            .subdir("s3"),
2148                    )
2149                    .child_default("b")
2150                    .build(),
2151            ),
2152            (
2153                "b",
2154                ComponentDeclBuilder::new()
2155                    .expose(
2156                        ExposeBuilder::directory()
2157                            .name("foo_data")
2158                            .source_static_child("c")
2159                            .subdir("s1/s2"),
2160                    )
2161                    .child_default("c")
2162                    .build(),
2163            ),
2164            (
2165                "c",
2166                ComponentDeclBuilder::new()
2167                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2168                    .expose(
2169                        ExposeBuilder::directory()
2170                            .name("foo_data")
2171                            .source(ExposeSource::Self_)
2172                            .rights(fio::R_STAR_DIR),
2173                    )
2174                    .build(),
2175            ),
2176        ];
2177        let model = T::new("a", components).build().await;
2178        model
2179            .create_static_file(Path::new("foo/s1/s2/s3/inner"), "hello")
2180            .await
2181            .expect("failed to create file");
2182        model
2183            .check_use_exposed_dir(
2184                Moniker::root(),
2185                CheckUse::Directory {
2186                    path: "/hippo_data".parse().unwrap(),
2187                    file: PathBuf::from("inner"),
2188                    expected_res: ExpectedResult::Ok,
2189                },
2190            )
2191            .await;
2192    }
2193
2194    pub async fn test_expose_from_self_and_child(&self) {
2195        let components = vec![
2196            ("a", ComponentDeclBuilder::new().child_default("b").build()),
2197            (
2198                "b",
2199                ComponentDeclBuilder::new()
2200                    .expose(
2201                        ExposeBuilder::directory()
2202                            .name("hippo_data")
2203                            .source_static_child("c")
2204                            .target_name("hippo_bar_data")
2205                            .rights(fio::R_STAR_DIR),
2206                    )
2207                    .expose(
2208                        ExposeBuilder::protocol()
2209                            .name("hippo")
2210                            .target_name("hippo_bar")
2211                            .source_static_child("c"),
2212                    )
2213                    .child_default("c")
2214                    .build(),
2215            ),
2216            (
2217                "c",
2218                ComponentDeclBuilder::new()
2219                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2220                    .protocol_default("foo")
2221                    .expose(
2222                        ExposeBuilder::directory()
2223                            .name("foo_data")
2224                            .source(ExposeSource::Self_)
2225                            .target_name("hippo_data")
2226                            .rights(fio::R_STAR_DIR),
2227                    )
2228                    .expose(
2229                        ExposeBuilder::protocol()
2230                            .name("foo")
2231                            .target_name("hippo")
2232                            .source(ExposeSource::Self_),
2233                    )
2234                    .build(),
2235            ),
2236        ];
2237        let model = T::new("a", components).build().await;
2238        model
2239            .check_use_exposed_dir(
2240                ["b"].try_into().unwrap(),
2241                CheckUse::Directory {
2242                    path: "/hippo_bar_data".parse().unwrap(),
2243                    file: PathBuf::from("hippo"),
2244                    expected_res: ExpectedResult::Ok,
2245                },
2246            )
2247            .await;
2248        model
2249            .check_use_exposed_dir(
2250                ["b"].try_into().unwrap(),
2251                CheckUse::Protocol {
2252                    path: "/hippo_bar".parse().unwrap(),
2253                    expected_res: ExpectedResult::Ok,
2254                },
2255            )
2256            .await;
2257        model
2258            .check_use_exposed_dir(
2259                ["b", "c"].try_into().unwrap(),
2260                CheckUse::Directory {
2261                    path: "/hippo_data".parse().unwrap(),
2262                    file: PathBuf::from("hippo"),
2263                    expected_res: ExpectedResult::Ok,
2264                },
2265            )
2266            .await;
2267        model
2268            .check_use_exposed_dir(
2269                ["b", "c"].try_into().unwrap(),
2270                CheckUse::Protocol {
2271                    path: "/hippo".parse().unwrap(),
2272                    expected_res: ExpectedResult::Ok,
2273                },
2274            )
2275            .await;
2276    }
2277
2278    pub async fn test_use_not_exposed(&self) {
2279        let components = vec![
2280            ("a", ComponentDeclBuilder::new().child_default("b").build()),
2281            ("b", ComponentDeclBuilder::new().child_default("c").build()),
2282            (
2283                "c",
2284                ComponentDeclBuilder::new()
2285                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2286                    .protocol_default("foo")
2287                    .expose(
2288                        ExposeBuilder::directory()
2289                            .name("foo_data")
2290                            .source(ExposeSource::Self_)
2291                            .target_name("hippo_data")
2292                            .rights(fio::R_STAR_DIR),
2293                    )
2294                    .expose(
2295                        ExposeBuilder::protocol()
2296                            .name("foo")
2297                            .target_name("hippo")
2298                            .source(ExposeSource::Self_),
2299                    )
2300                    .build(),
2301            ),
2302        ];
2303        let model = T::new("a", components).build().await;
2304        // Capability is only exposed from "c", so it only be usable from there.
2305
2306        // When trying to open a capability that's not exposed to realm, there's no node for it in the
2307        // exposed dir, so no routing takes place.
2308        model
2309            .check_use_exposed_dir(
2310                ["b"].try_into().unwrap(),
2311                CheckUse::Directory {
2312                    path: "/hippo_data".parse().unwrap(),
2313                    file: PathBuf::from("hippo"),
2314                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2315                },
2316            )
2317            .await;
2318        model
2319            .check_use_exposed_dir(
2320                ["b"].try_into().unwrap(),
2321                CheckUse::Protocol {
2322                    path: "/hippo".parse().unwrap(),
2323                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2324                },
2325            )
2326            .await;
2327        model
2328            .check_use_exposed_dir(
2329                ["b", "c"].try_into().unwrap(),
2330                CheckUse::Directory {
2331                    path: "/hippo_data".parse().unwrap(),
2332                    file: PathBuf::from("hippo"),
2333                    expected_res: ExpectedResult::Ok,
2334                },
2335            )
2336            .await;
2337        model
2338            .check_use_exposed_dir(
2339                ["b", "c"].try_into().unwrap(),
2340                CheckUse::Protocol {
2341                    path: "/hippo".parse().unwrap(),
2342                    expected_res: ExpectedResult::Ok,
2343                },
2344            )
2345            .await;
2346    }
2347
2348    pub async fn test_expose_to_framework_from_self(&self) {
2349        let components = vec![(
2350            "a",
2351            ComponentDeclBuilder::new()
2352                .protocol_default("foo")
2353                .expose(
2354                    ExposeBuilder::protocol()
2355                        .name("foo")
2356                        .target_name("hippo")
2357                        .target(ExposeTarget::Framework)
2358                        .source(ExposeSource::Self_),
2359                )
2360                .build(),
2361        )];
2362        let model = T::new("a", components).build().await;
2363        model
2364            .check_exposed_to_framework(
2365                Moniker::root(),
2366                CheckUse::Protocol {
2367                    path: "/hippo".parse().unwrap(),
2368                    expected_res: ExpectedResult::Ok,
2369                },
2370            )
2371            .await;
2372    }
2373
2374    pub async fn test_expose_to_framework_from_child(&self) {
2375        let components = vec![
2376            (
2377                "a",
2378                ComponentDeclBuilder::new()
2379                    .child_default("b")
2380                    .expose(
2381                        ExposeBuilder::protocol()
2382                            .name("foo")
2383                            .target_name("hippo")
2384                            .target(ExposeTarget::Framework)
2385                            .source(ExposeSource::Child("b".parse().unwrap())),
2386                    )
2387                    .build(),
2388            ),
2389            (
2390                "b",
2391                ComponentDeclBuilder::new()
2392                    .protocol_default("foo")
2393                    .expose(ExposeBuilder::protocol().name("foo").source(ExposeSource::Self_))
2394                    .build(),
2395            ),
2396        ];
2397        let model = T::new("a", components).build().await;
2398        model
2399            .check_exposed_to_framework(
2400                Moniker::root(),
2401                CheckUse::Protocol {
2402                    path: "/hippo".parse().unwrap(),
2403                    expected_res: ExpectedResult::Ok,
2404                },
2405            )
2406            .await;
2407    }
2408
2409    pub async fn test_expose_to_parent_and_framework(&self) {
2410        let components = vec![
2411            ("a", ComponentDeclBuilder::new().child_default("b").build()),
2412            (
2413                "b",
2414                ComponentDeclBuilder::new()
2415                    .protocol_default("foo")
2416                    .expose(
2417                        ExposeBuilder::protocol()
2418                            .name("foo")
2419                            .target_name("hippo")
2420                            .source(ExposeSource::Self_),
2421                    )
2422                    .expose(
2423                        ExposeBuilder::protocol()
2424                            .name("foo")
2425                            .target(ExposeTarget::Framework)
2426                            .source(ExposeSource::Self_),
2427                    )
2428                    .build(),
2429            ),
2430        ];
2431        let model = T::new("a", components).build().await;
2432        model
2433            .check_exposed_to_framework(
2434                ["b"].try_into().unwrap(),
2435                CheckUse::Protocol {
2436                    path: "/hippo".parse().unwrap(),
2437                    expected_res: ExpectedResult::Ok,
2438                },
2439            )
2440            .await;
2441        model
2442            .check_use_exposed_dir(
2443                ["b"].try_into().unwrap(),
2444                CheckUse::Protocol {
2445                    path: "/hippo".parse().unwrap(),
2446                    expected_res: ExpectedResult::Ok,
2447                },
2448            )
2449            .await;
2450    }
2451
2452    ///   (cm)
2453    ///    |
2454    ///    a
2455    ///
2456    /// a: uses an invalid service from the component manager.
2457    pub async fn test_invalid_use_from_component_manager(&self) {
2458        let components = vec![(
2459            "a",
2460            ComponentDeclBuilder::new()
2461                .use_(UseBuilder::protocol().name("invalid").path("/svc/valid"))
2462                .build(),
2463        )];
2464
2465        // Try and use the service. We expect a failure.
2466        let model = T::new("a", components).build().await;
2467        model
2468            .check_use(
2469                Moniker::root(),
2470                CheckUse::Protocol {
2471                    path: "/svc/valid".parse().unwrap(),
2472                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2473                },
2474            )
2475            .await;
2476    }
2477
2478    ///   (cm)
2479    ///    |
2480    ///    a
2481    ///    |
2482    ///    b
2483    ///
2484    /// a: offers an invalid service from the component manager to "b".
2485    /// b: attempts to use the service
2486    pub async fn test_invalid_offer_from_component_manager(&self) {
2487        let components = vec![
2488            (
2489                "a",
2490                ComponentDeclBuilder::new()
2491                    .offer(
2492                        OfferBuilder::protocol()
2493                            .name("invalid")
2494                            .target_name("valid")
2495                            .source(OfferSource::Parent)
2496                            .target_static_child("b"),
2497                    )
2498                    .child_default("b")
2499                    .build(),
2500            ),
2501            ("b", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("valid")).build()),
2502        ];
2503
2504        // Try and use the service. We expect a failure.
2505        let model = T::new("a", components).build().await;
2506        model
2507            .check_use(
2508                ["b"].try_into().unwrap(),
2509                CheckUse::Protocol {
2510                    path: "/svc/valid".parse().unwrap(),
2511                    expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2512                },
2513            )
2514            .await;
2515    }
2516
2517    /*
2518        TODO(https://fxbug.dev/42059303): Allow exposing from parent.
2519
2520        /// Tests exposing an event_stream from a child through its parent down to another
2521        /// unrelated child.
2522        ///        a
2523        ///         \
2524        ///          b
2525        ///          /\
2526        ///          c f
2527        ///          /\
2528        ///          d e
2529        /// c exposes started with a scope of e (but not d)
2530        /// to b, which then offers that to f.
2531        pub async fn test_expose_event_stream_with_scope(&self) {
2532            let components = vec![
2533                ("a", ComponentDeclBuilder::new().child_default("b").build()),
2534                (
2535                    "b",
2536                    ComponentDeclBuilder::new()
2537                        .offer(OfferDecl::EventStream(OfferEventStreamDecl {
2538                            source: OfferSource::Child(ChildRef {
2539                                name: "c".to_string(),
2540                                collection: None,
2541                            }),
2542                            source_name: "started".parse().unwrap(),
2543                            scope: None,
2544                            filter: None,
2545                            target: OfferTarget::Child(ChildRef {
2546                                name: "f".to_string(),
2547                                collection: None,
2548                            }),
2549                            target_name: "started".parse().unwrap(),
2550                            availability: Availability::Required,
2551                        }))
2552                        .child_default("c")
2553                        .child_default("f")
2554                        .build(),
2555                ),
2556                (
2557                    "c",
2558                    ComponentDeclBuilder::new()
2559                        .expose(ExposeDecl::EventStream(ExposeEventStreamDecl {
2560                            source: ExposeSource::Framework,
2561                            source_name: "started".parse().unwrap(),
2562    source_dictionary: Default::default(),
2563                            scope: Some(vec![EventScope::Child(ChildRef {
2564                                name: "e".to_string(),
2565                                collection: None,
2566                            })]),
2567                            target: ExposeTarget::Parent,
2568                            target_name: "started".parse().unwrap(),
2569                        }))
2570                        .child_default("d")
2571                        .child_default("e")
2572                        .build(),
2573                ),
2574                ("d", ComponentDeclBuilder::new().build()),
2575                ("e", ComponentDeclBuilder::new().build()),
2576                (
2577                    "f",
2578                    ComponentDeclBuilder::new()
2579                        .use_(UseBuilder::event_stream()
2580                            .name("started")
2581                            .path("/event/stream"))
2582                        .build()
2583                ),
2584            ];
2585
2586            let mut builder = T::new("a", components);
2587            builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2588                name: "started".parse().unwrap(),
2589            })]);
2590
2591            let model = builder.build().await;
2592            model
2593                .check_use(
2594                    vec!["b", "f"].into(),
2595                    CheckUse::EventStream {
2596                        expected_res: ExpectedResult::Ok,
2597                        path: "/event/stream".parse().unwrap(),
2598                        scope: vec![
2599                            ComponentEventRoute {
2600                                component: "c".to_string(),
2601                                scope: Some(vec!["e".to_string()]),
2602                            },
2603                            ComponentEventRoute { component: "b".to_string(), scope: None },
2604                        ],
2605                        name: "started".parse().unwrap(),
2606                    },
2607                )
2608                .await;
2609        }*/
2610
2611    /// Tests event stream aliasing (scoping rules are applied correctly)
2612    ///        root
2613    ///        |
2614    ///        a
2615    ///       /|\
2616    ///      b c d
2617    /// A offers started to b with scope b, A offers started to c with scope d,
2618    /// A offers started to d with scope c.
2619    pub async fn test_event_stream_aliasing(&self) {
2620        let components = vec![
2621            ("root", ComponentDeclBuilder::new().child_default("a").build()),
2622            (
2623                "a",
2624                ComponentDeclBuilder::new()
2625                    .offer(
2626                        OfferBuilder::event_stream()
2627                            .name("started")
2628                            .source(OfferSource::Parent)
2629                            .target(OfferTarget::Child(ChildRef {
2630                                name: "b".parse().unwrap(),
2631                                collection: None,
2632                            }))
2633                            .scope(vec![EventScope::Child(ChildRef {
2634                                name: "b".parse().unwrap(),
2635                                collection: None,
2636                            })]),
2637                    )
2638                    .offer(
2639                        OfferBuilder::event_stream()
2640                            .name("started")
2641                            .source(OfferSource::Parent)
2642                            .target(OfferTarget::Child(ChildRef {
2643                                name: "d".parse().unwrap(),
2644                                collection: None,
2645                            }))
2646                            .scope(vec![EventScope::Child(ChildRef {
2647                                name: "c".parse().unwrap(),
2648                                collection: None,
2649                            })]),
2650                    )
2651                    .offer(
2652                        OfferBuilder::event_stream()
2653                            .name("started")
2654                            .source(OfferSource::Parent)
2655                            .target(OfferTarget::Child(ChildRef {
2656                                name: "c".parse().unwrap(),
2657                                collection: None,
2658                            }))
2659                            .scope(vec![EventScope::Child(ChildRef {
2660                                name: "d".parse().unwrap(),
2661                                collection: None,
2662                            })]),
2663                    )
2664                    .child_default("b")
2665                    .child_default("c")
2666                    .child_default("d")
2667                    .build(),
2668            ),
2669            (
2670                "b",
2671                ComponentDeclBuilder::new()
2672                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2673                    .build(),
2674            ),
2675            (
2676                "c",
2677                ComponentDeclBuilder::new()
2678                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2679                    .build(),
2680            ),
2681            (
2682                "d",
2683                ComponentDeclBuilder::new()
2684                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2685                    .build(),
2686            ),
2687        ];
2688
2689        let mut builder = T::new("a", components);
2690        builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2691            name: "started".parse().unwrap(),
2692        })]);
2693
2694        let model = builder.build().await;
2695        model
2696            .check_use(
2697                ["b"].try_into().unwrap(),
2698                CheckUse::EventStream {
2699                    expected_res: ExpectedResult::Ok,
2700                    path: "/event/stream".parse().unwrap(),
2701                    scope: vec![ComponentEventRoute {
2702                        component: "/".to_string(),
2703                        scope: Some(vec!["b".to_string()]),
2704                    }],
2705                    name: "started".parse().unwrap(),
2706                },
2707            )
2708            .await;
2709        model
2710            .check_use(
2711                ["c"].try_into().unwrap(),
2712                CheckUse::EventStream {
2713                    expected_res: ExpectedResult::Ok,
2714                    path: "/event/stream".parse().unwrap(),
2715                    scope: vec![ComponentEventRoute {
2716                        component: "/".to_string(),
2717                        scope: Some(vec!["d".to_string()]),
2718                    }],
2719                    name: "started".parse().unwrap(),
2720                },
2721            )
2722            .await;
2723
2724        model
2725            .check_use(
2726                ["d"].try_into().unwrap(),
2727                CheckUse::EventStream {
2728                    expected_res: ExpectedResult::Ok,
2729                    path: "/event/stream".parse().unwrap(),
2730                    scope: vec![ComponentEventRoute {
2731                        component: "/".to_string(),
2732                        scope: Some(vec!["c".to_string()]),
2733                    }],
2734                    name: "started".parse().unwrap(),
2735                },
2736            )
2737            .await;
2738    }
2739
2740    ///   a
2741    ///    \
2742    ///     b
2743    ///
2744    /// b: uses framework events "started", and "capability_requested"
2745    pub async fn test_use_event_stream_from_above_root(&self) {
2746        let components = vec![(
2747            "a",
2748            ComponentDeclBuilder::new()
2749                .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2750                .build(),
2751        )];
2752
2753        let mut builder = T::new("a", components);
2754        builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2755            name: "started".parse().unwrap(),
2756        })]);
2757
2758        let model = builder.build().await;
2759        model
2760            .check_use(
2761                Moniker::root(),
2762                CheckUse::EventStream {
2763                    expected_res: ExpectedResult::Ok,
2764                    path: "/event/stream".parse().unwrap(),
2765                    scope: vec![],
2766                    name: "started".parse().unwrap(),
2767                },
2768            )
2769            .await;
2770    }
2771
2772    ///   a
2773    ///   /\
2774    ///  b  c
2775    ///    / \
2776    ///   d   e
2777    /// c: uses framework events "started", and "capability_requested",
2778    /// scoped to b and c.
2779    /// d receives started which is scoped to b, c, and e.
2780    pub async fn test_use_event_stream_from_above_root_and_downscoped(&self) {
2781        let components = vec![
2782            (
2783                "a",
2784                ComponentDeclBuilder::new()
2785                    .offer(
2786                        OfferBuilder::event_stream()
2787                            .name("started")
2788                            .source(OfferSource::Parent)
2789                            .target(OfferTarget::Child(ChildRef {
2790                                name: "b".parse().unwrap(),
2791                                collection: None,
2792                            }))
2793                            .scope(vec![
2794                                EventScope::Child(ChildRef {
2795                                    name: "b".parse().unwrap(),
2796                                    collection: None,
2797                                }),
2798                                EventScope::Child(ChildRef {
2799                                    name: "c".parse().unwrap(),
2800                                    collection: None,
2801                                }),
2802                            ]),
2803                    )
2804                    .offer(
2805                        OfferBuilder::event_stream()
2806                            .name("started")
2807                            .source(OfferSource::Parent)
2808                            .target(OfferTarget::Child(ChildRef {
2809                                name: "c".parse().unwrap(),
2810                                collection: None,
2811                            }))
2812                            .scope(vec![
2813                                EventScope::Child(ChildRef {
2814                                    name: "b".parse().unwrap(),
2815                                    collection: None,
2816                                }),
2817                                EventScope::Child(ChildRef {
2818                                    name: "c".parse().unwrap(),
2819                                    collection: None,
2820                                }),
2821                            ]),
2822                    )
2823                    .child_default("b")
2824                    .child_default("c")
2825                    .build(),
2826            ),
2827            (
2828                "b",
2829                ComponentDeclBuilder::new()
2830                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2831                    .build(),
2832            ),
2833            (
2834                "c",
2835                ComponentDeclBuilder::new()
2836                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2837                    .offer(
2838                        OfferBuilder::event_stream()
2839                            .name("started")
2840                            .source(OfferSource::Parent)
2841                            .target(OfferTarget::Child(ChildRef {
2842                                name: "d".parse().unwrap(),
2843                                collection: None,
2844                            }))
2845                            .scope(vec![EventScope::Child(ChildRef {
2846                                name: "e".parse().unwrap(),
2847                                collection: None,
2848                            })]),
2849                    )
2850                    .child_default("d")
2851                    .child_default("e")
2852                    .build(),
2853            ),
2854            (
2855                "d",
2856                ComponentDeclBuilder::new()
2857                    .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2858                    .build(),
2859            ),
2860            ("e", ComponentDeclBuilder::new().build()),
2861        ];
2862
2863        let mut builder = T::new("a", components);
2864        builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2865            name: "started".parse().unwrap(),
2866        })]);
2867
2868        let model = builder.build().await;
2869        model
2870            .check_use(
2871                ["b"].try_into().unwrap(),
2872                CheckUse::EventStream {
2873                    expected_res: ExpectedResult::Ok,
2874                    path: "/event/stream".parse().unwrap(),
2875                    scope: vec![ComponentEventRoute {
2876                        component: "/".to_string(),
2877                        scope: Some(vec!["b".to_string(), "c".to_string()]),
2878                    }],
2879                    name: "started".parse().unwrap(),
2880                },
2881            )
2882            .await;
2883        model
2884            .check_use(
2885                ["c"].try_into().unwrap(),
2886                CheckUse::EventStream {
2887                    expected_res: ExpectedResult::Ok,
2888                    path: "/event/stream".parse().unwrap(),
2889                    scope: vec![ComponentEventRoute {
2890                        component: "/".to_string(),
2891                        scope: Some(vec!["b".to_string(), "c".to_string()]),
2892                    }],
2893                    name: "started".parse().unwrap(),
2894                },
2895            )
2896            .await;
2897        model
2898            .check_use(
2899                ["c", "d"].try_into().unwrap(), // Should get e's event from parent
2900                CheckUse::EventStream {
2901                    expected_res: ExpectedResult::Ok,
2902                    path: "/event/stream".parse().unwrap(),
2903                    scope: vec![
2904                        ComponentEventRoute {
2905                            component: "/".to_string(),
2906                            scope: Some(vec!["b".to_string(), "c".to_string()]),
2907                        },
2908                        ComponentEventRoute {
2909                            component: "c".to_string(),
2910                            scope: Some(vec!["e".to_string()]),
2911                        },
2912                    ],
2913                    name: "started".parse().unwrap(),
2914                },
2915            )
2916            .await;
2917    }
2918
2919    ///   a
2920    ///    \
2921    ///     b
2922    ///
2923    /// a; attempts to offer event "capability_requested" to b.
2924    pub async fn test_can_offer_capability_requested_event(&self) {
2925        let components = vec![
2926            (
2927                "a",
2928                ComponentDeclBuilder::new()
2929                    .offer(
2930                        OfferBuilder::event_stream()
2931                            .name("capability_requested")
2932                            .target_name("capability_requested_on_a")
2933                            .source(OfferSource::Parent)
2934                            .target_static_child("b"),
2935                    )
2936                    .child_default("b")
2937                    .build(),
2938            ),
2939            (
2940                "b",
2941                ComponentDeclBuilder::new()
2942                    .use_(UseBuilder::event_stream().name("capability_requested_on_a"))
2943                    .build(),
2944            ),
2945        ];
2946
2947        let mut builder = T::new("a", components);
2948        builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2949            name: "capability_requested".parse().unwrap(),
2950        })]);
2951        let model = builder.build().await;
2952
2953        model
2954            .check_use(
2955                ["b"].try_into().unwrap(),
2956                CheckUse::EventStream {
2957                    expected_res: ExpectedResult::Ok,
2958                    path: "/svc/fuchsia.component.EventStream".parse().unwrap(),
2959                    scope: vec![ComponentEventRoute { component: "/".to_string(), scope: None }],
2960                    name: "capability_requested_on_a".parse().unwrap(),
2961                },
2962            )
2963            .await;
2964    }
2965
2966    ///   a
2967    ///    \
2968    ///     b
2969    ///
2970    /// b: uses service /svc/hippo as /svc/hippo.
2971    /// a: provides b with the service but policy prevents it.
2972    pub async fn test_use_protocol_denied_by_capability_policy(&self) {
2973        let components = vec![
2974            (
2975                "a",
2976                ComponentDeclBuilder::new()
2977                    .protocol_default("hippo")
2978                    .offer(
2979                        OfferBuilder::protocol()
2980                            .name("hippo")
2981                            .source(OfferSource::Self_)
2982                            .target_static_child("b"),
2983                    )
2984                    .child_default("b")
2985                    .build(),
2986            ),
2987            ("b", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).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            HashSet::new(),
2998        );
2999
3000        let model = builder.build().await;
3001        model
3002            .check_use(
3003                ["b"].try_into().unwrap(),
3004                CheckUse::Protocol {
3005                    path: default_service_capability(),
3006                    expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
3007                },
3008            )
3009            .await;
3010    }
3011
3012    ///   a
3013    ///    \
3014    ///     b
3015    ///
3016    /// b: uses directory /data/foo as /data/bar.
3017    /// a: provides b with the directory but policy prevents it.
3018    pub async fn test_use_directory_with_alias_denied_by_capability_policy(&self) {
3019        let components = vec![
3020            (
3021                "a",
3022                ComponentDeclBuilder::new()
3023                    .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
3024                    .offer(
3025                        OfferBuilder::directory()
3026                            .name("foo_data")
3027                            .target_name("bar_data")
3028                            .source(OfferSource::Self_)
3029                            .target_static_child("b")
3030                            .rights(fio::R_STAR_DIR),
3031                    )
3032                    .child_default("b")
3033                    .build(),
3034            ),
3035            (
3036                "b",
3037                ComponentDeclBuilder::new()
3038                    .use_(UseBuilder::directory().name("bar_data").path("/data/hippo"))
3039                    .build(),
3040            ),
3041        ];
3042        let mut builder = T::new("a", components);
3043        builder.add_capability_policy(
3044            CapabilityAllowlistKey {
3045                source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
3046                source_name: "foo_data".parse().unwrap(),
3047                source: CapabilityAllowlistSource::Self_,
3048                capability: CapabilityTypeName::Directory,
3049            },
3050            HashSet::new(),
3051        );
3052        let model = builder.build().await;
3053        model
3054            .check_use(
3055                ["b"].try_into().unwrap(),
3056                CheckUse::default_directory(ExpectedResult::Err(zx::Status::ACCESS_DENIED)),
3057            )
3058            .await;
3059    }
3060
3061    ///   a
3062    ///    \
3063    ///     b
3064    ///      \
3065    ///       c
3066    /// c: uses service /svc/hippo as /svc/hippo.
3067    /// b: uses service /svc/hippo as /svc/hippo.
3068    /// a: provides b with the service policy allows it.
3069    /// b: provides c with the service policy does not allow it.
3070    pub async fn test_use_protocol_partial_chain_allowed_by_capability_policy(&self) {
3071        let components = vec![
3072            (
3073                "a",
3074                ComponentDeclBuilder::new()
3075                    .protocol_default("hippo")
3076                    .offer(
3077                        OfferBuilder::protocol()
3078                            .name("hippo")
3079                            .source(OfferSource::Self_)
3080                            .target_static_child("b"),
3081                    )
3082                    .child_default("b")
3083                    .build(),
3084            ),
3085            (
3086                "b",
3087                ComponentDeclBuilder::new()
3088                    .offer(
3089                        OfferBuilder::protocol()
3090                            .name("hippo")
3091                            .source(OfferSource::Parent)
3092                            .target_static_child("c"),
3093                    )
3094                    .use_(UseBuilder::protocol().name("hippo"))
3095                    .child_default("c")
3096                    .build(),
3097            ),
3098            ("c", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
3099        ];
3100
3101        let mut allowlist = HashSet::new();
3102        allowlist.insert(AllowlistEntryBuilder::new().exact("b").build());
3103
3104        let mut builder = T::new("a", components);
3105        builder.add_capability_policy(
3106            CapabilityAllowlistKey {
3107                source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
3108                source_name: "hippo".parse().unwrap(),
3109                source: CapabilityAllowlistSource::Self_,
3110                capability: CapabilityTypeName::Protocol,
3111            },
3112            allowlist,
3113        );
3114        let model = builder.build().await;
3115
3116        model
3117            .check_use(
3118                ["b"].try_into().unwrap(),
3119                CheckUse::Protocol {
3120                    path: default_service_capability(),
3121                    expected_res: ExpectedResult::Ok,
3122                },
3123            )
3124            .await;
3125
3126        model
3127            .check_use(
3128                ["b", "c"].try_into().unwrap(),
3129                CheckUse::Protocol {
3130                    path: default_service_capability(),
3131                    expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
3132                },
3133            )
3134            .await;
3135    }
3136
3137    ///   a
3138    ///    \
3139    ///     b
3140    ///    /  \
3141    ///   c    d
3142    /// b: provides d with the service policy allows denies it.
3143    /// b: provides c with the service policy allows it.
3144    /// c: uses service /svc/hippo as /svc/hippo.
3145    /// Tests component provided caps in the middle of a path
3146    pub async fn test_use_protocol_component_provided_capability_policy(&self) {
3147        let components = vec![
3148            (
3149                "a",
3150                ComponentDeclBuilder::new()
3151                    .protocol_default("hippo")
3152                    .offer(
3153                        OfferBuilder::protocol()
3154                            .name("hippo")
3155                            .source(OfferSource::Self_)
3156                            .target_static_child("b"),
3157                    )
3158                    .child_default("b")
3159                    .build(),
3160            ),
3161            (
3162                "b",
3163                ComponentDeclBuilder::new()
3164                    .offer(
3165                        OfferBuilder::protocol()
3166                            .name("hippo")
3167                            .source(OfferSource::Parent)
3168                            .target_static_child("c"),
3169                    )
3170                    .offer(
3171                        OfferBuilder::protocol()
3172                            .name("hippo")
3173                            .source(OfferSource::Parent)
3174                            .target_static_child("d"),
3175                    )
3176                    .child_default("c")
3177                    .child_default("d")
3178                    .build(),
3179            ),
3180            ("c", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
3181            ("d", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
3182        ];
3183
3184        let mut allowlist = HashSet::new();
3185        allowlist.insert(AllowlistEntryBuilder::new().exact("b").build());
3186        allowlist.insert(AllowlistEntryBuilder::new().exact("b").exact("c").build());
3187
3188        let mut builder = T::new("a", components);
3189        builder.add_capability_policy(
3190            CapabilityAllowlistKey {
3191                source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
3192                source_name: "hippo".parse().unwrap(),
3193                source: CapabilityAllowlistSource::Self_,
3194                capability: CapabilityTypeName::Protocol,
3195            },
3196            allowlist,
3197        );
3198        let model = builder.build().await;
3199
3200        model
3201            .check_use(
3202                ["b", "c"].try_into().unwrap(),
3203                CheckUse::Protocol {
3204                    path: default_service_capability(),
3205                    expected_res: ExpectedResult::Ok,
3206                },
3207            )
3208            .await;
3209
3210        model
3211            .check_use(
3212                ["b", "d"].try_into().unwrap(),
3213                CheckUse::Protocol {
3214                    path: default_service_capability(),
3215                    expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
3216                },
3217            )
3218            .await;
3219    }
3220
3221    ///  component manager's namespace
3222    ///   |
3223    ///   a
3224    ///
3225    /// a: uses service /use_from_cm_namespace/svc/foo as foo
3226    pub async fn test_use_from_component_manager_namespace_denied_by_policy(&self) {
3227        let components = vec![(
3228            "a",
3229            ComponentDeclBuilder::new()
3230                .use_(UseBuilder::protocol().name("foo").path("/svc/hippo"))
3231                .build(),
3232        )];
3233        let namespace_capabilities = vec![
3234            CapabilityBuilder::protocol()
3235                .name("foo")
3236                .path("/use_from_cm_namespace/svc/foo")
3237                .build(),
3238        ];
3239        let mut builder = T::new("a", components);
3240        builder.set_namespace_capabilities(namespace_capabilities);
3241        builder.add_capability_policy(
3242            CapabilityAllowlistKey {
3243                source_moniker: ExtendedMoniker::ComponentManager,
3244                source_name: "foo".parse().unwrap(),
3245                source: CapabilityAllowlistSource::Self_,
3246                capability: CapabilityTypeName::Protocol,
3247            },
3248            HashSet::new(),
3249        );
3250        let model = builder.build().await;
3251
3252        model.install_namespace_directory("/use_from_cm_namespace");
3253        model
3254            .check_use(
3255                Moniker::root(),
3256                CheckUse::Protocol {
3257                    path: default_service_capability(),
3258                    expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
3259                },
3260            )
3261            .await;
3262    }
3263
3264    ///   a
3265    ///  /
3266    /// b
3267    ///
3268    /// a: offer to b from self
3269    /// b: use from parent
3270    pub async fn test_route_service_from_parent(&self) {
3271        let use_decl = UseBuilder::service().name("foo").build();
3272        let components = vec![
3273            (
3274                "a",
3275                ComponentDeclBuilder::new()
3276                    .offer(
3277                        OfferBuilder::service()
3278                            .name("foo")
3279                            .source(OfferSource::Self_)
3280                            .target_static_child("b"),
3281                    )
3282                    .service_default("foo")
3283                    .child_default("b")
3284                    .build(),
3285            ),
3286            ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3287        ];
3288        let model = T::new("a", components).build().await;
3289        let b_component =
3290            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3291        let a_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
3292        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3293        let source = route_capability(
3294            RouteRequest::UseService(use_decl),
3295            &b_component,
3296            &mut NoopRouteMapper,
3297        )
3298        .await
3299        .expect("failed to route service");
3300        match source {
3301            RouteSource {
3302                source:
3303                    CapabilitySource::Component(ComponentSource {
3304                        capability: ComponentCapability::Service(ServiceDecl { name, source_path }),
3305                        moniker,
3306                    }),
3307                relative_path,
3308            } if relative_path.is_dot() => {
3309                assert_eq!(name, "foo");
3310                assert_eq!(
3311                    source_path.expect("missing source path"),
3312                    "/svc/foo".parse::<cm_types::Path>().unwrap()
3313                );
3314                assert_eq!(&moniker, a_component.moniker());
3315            }
3316            _ => panic!("bad capability source"),
3317        };
3318    }
3319
3320    ///   a
3321    ///  /
3322    /// b
3323    ///
3324    /// a: use from #b
3325    /// b: expose to parent from self
3326    pub async fn test_route_service_from_child(&self) {
3327        let use_decl = UseBuilder::service().name("foo").source_static_child("b").build();
3328        let components = vec![
3329            ("a", ComponentDeclBuilder::new().use_(use_decl.clone()).child_default("b").build()),
3330            (
3331                "b",
3332                ComponentDeclBuilder::new()
3333                    .service_default("foo")
3334                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3335                    .build(),
3336            ),
3337        ];
3338        let model = T::new("a", components).build().await;
3339        let a_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
3340        let b_component =
3341            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3342        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3343        let source = route_capability(
3344            RouteRequest::UseService(use_decl),
3345            &a_component,
3346            &mut NoopRouteMapper,
3347        )
3348        .await
3349        .expect("failed to route service");
3350        match source {
3351            RouteSource {
3352                source:
3353                    CapabilitySource::Component(ComponentSource {
3354                        capability: ComponentCapability::Service(ServiceDecl { name, source_path }),
3355                        moniker,
3356                    }),
3357                relative_path,
3358            } if relative_path.is_dot() => {
3359                assert_eq!(name, "foo");
3360                assert_eq!(
3361                    source_path.expect("missing source path"),
3362                    "/svc/foo".parse::<cm_types::Path>().unwrap()
3363                );
3364                assert_eq!(&moniker, b_component.moniker());
3365            }
3366            _ => panic!("bad capability source"),
3367        };
3368    }
3369
3370    ///   a
3371    ///  / \
3372    /// b   c
3373    ///
3374    /// a: offer to b from child c
3375    /// b: use from parent
3376    /// c: expose from self
3377    pub async fn test_route_service_from_sibling(&self) {
3378        let use_decl = UseBuilder::service().name("foo").build();
3379        let components = vec![
3380            (
3381                "a",
3382                ComponentDeclBuilder::new()
3383                    .offer(
3384                        OfferBuilder::service()
3385                            .name("foo")
3386                            .source_static_child("c")
3387                            .target_static_child("b"),
3388                    )
3389                    .child_default("b")
3390                    .child_default("c")
3391                    .build(),
3392            ),
3393            ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3394            (
3395                "c",
3396                ComponentDeclBuilder::new()
3397                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3398                    .service_default("foo")
3399                    .build(),
3400            ),
3401        ];
3402        let model = T::new("a", components).build().await;
3403        let b_component =
3404            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3405        let c_component =
3406            model.look_up_instance(&["c"].try_into().unwrap()).await.expect("c instance");
3407        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3408        let source = route_capability(
3409            RouteRequest::UseService(use_decl),
3410            &b_component,
3411            &mut NoopRouteMapper,
3412        )
3413        .await
3414        .expect("failed to route service");
3415
3416        // Verify this source comes from `c`.
3417        match source {
3418            RouteSource {
3419                source:
3420                    CapabilitySource::Component(ComponentSource {
3421                        capability: ComponentCapability::Service(ServiceDecl { name, source_path }),
3422                        moniker,
3423                    }),
3424                relative_path,
3425            } if relative_path.is_dot() => {
3426                assert_eq!(name, "foo");
3427                assert_eq!(
3428                    source_path.expect("missing source path"),
3429                    "/svc/foo".parse::<cm_types::Path>().unwrap()
3430                );
3431                assert_eq!(&moniker, c_component.moniker());
3432            }
3433            _ => panic!("bad capability source"),
3434        };
3435    }
3436
3437    ///   a
3438    ///  / \
3439    /// b   c
3440    ///
3441    /// a: offer to b with service instance filter set from child c
3442    /// b: use from parent
3443    /// c: expose from self
3444    pub async fn test_route_filtered_service_from_sibling(&self) {
3445        let use_decl = UseBuilder::service().name("foo").build();
3446        let components = vec![
3447            (
3448                "a",
3449                ComponentDeclBuilder::new()
3450                    .offer(
3451                        OfferBuilder::service()
3452                            .name("foo")
3453                            .source_static_child("c")
3454                            .target_static_child("b")
3455                            .source_instance_filter(["service_instance_0"])
3456                            .renamed_instances([]),
3457                    )
3458                    .child_default("b")
3459                    .child_default("c")
3460                    .build(),
3461            ),
3462            ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3463            (
3464                "c",
3465                ComponentDeclBuilder::new()
3466                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3467                    .service_default("foo")
3468                    .build(),
3469            ),
3470        ];
3471        let model = T::new("a", components).build().await;
3472        let b_component =
3473            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3474        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3475        let source = route_capability(
3476            RouteRequest::UseService(use_decl),
3477            &b_component,
3478            &mut NoopRouteMapper,
3479        )
3480        .await
3481        .expect("failed to route service");
3482
3483        // Verify this source comes from `c`.
3484        let RouteSource { source, relative_path } = source;
3485        assert!(relative_path.is_dot(), "unexpected capability path");
3486        assert_eq!(source.source_name(), Some(&"foo".parse().unwrap()));
3487        assert_eq!(
3488            source.source_moniker(),
3489            ExtendedMoniker::ComponentInstance("c".parse().unwrap())
3490        );
3491    }
3492
3493    ///   a
3494    ///  / \
3495    /// b   c
3496    ///
3497    /// a: offer to b with a service instance renamed from child c
3498    /// b: use from parent
3499    /// c: expose from self
3500    pub async fn test_route_renamed_service_instance_from_sibling(&self) {
3501        let use_decl = UseBuilder::service().name("foo").build();
3502        let components = vec![
3503            (
3504                "a",
3505                ComponentDeclBuilder::new()
3506                    .offer(
3507                        OfferBuilder::service()
3508                            .name("foo")
3509                            .source_static_child("c")
3510                            .target_static_child("b")
3511                            .renamed_instances([("instance_0", "renamed_instance_0")]),
3512                    )
3513                    .child_default("b")
3514                    .child_default("c")
3515                    .build(),
3516            ),
3517            ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3518            (
3519                "c",
3520                ComponentDeclBuilder::new()
3521                    .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3522                    .service_default("foo")
3523                    .build(),
3524            ),
3525        ];
3526        let model = T::new("a", components).build().await;
3527        let b_component =
3528            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3529        let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3530        let source = route_capability(
3531            RouteRequest::UseService(use_decl),
3532            &b_component,
3533            &mut NoopRouteMapper,
3534        )
3535        .await
3536        .expect("failed to route service");
3537
3538        // Verify this source comes from `c`.
3539        let RouteSource { source, relative_path } = source;
3540        assert!(relative_path.is_dot(), "unexpected capability path");
3541        assert_eq!(source.source_name(), Some(&"foo".parse().unwrap()));
3542        assert_eq!(
3543            source.source_moniker(),
3544            ExtendedMoniker::ComponentInstance("c".parse().unwrap())
3545        );
3546    }
3547
3548    ///  a
3549    ///   \
3550    ///    b
3551    ///
3552    /// a: declares runner "elf" with service format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from "self".
3553    /// a: registers runner "elf" from self in environment as "hobbit".
3554    /// b: uses runner "hobbit".
3555    pub async fn test_route_runner_from_parent_environment(&self) {
3556        let components = vec![
3557            (
3558                "a",
3559                ComponentDeclBuilder::new()
3560                    .child(ChildBuilder::new().name("b").environment("env"))
3561                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3562                        source_name: "elf".parse().unwrap(),
3563                        source: RegistrationSource::Self_,
3564                        target_name: "hobbit".parse().unwrap(),
3565                    }))
3566                    .runner_default("elf")
3567                    .build(),
3568            ),
3569            ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3570        ];
3571
3572        let model = T::new("a", components).build().await;
3573        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3574        let b_component =
3575            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3576        let source = route_capability(
3577            RouteRequest::UseRunner(UseRunnerDecl {
3578                source: UseSource::Environment,
3579                source_name: "hobbit".parse().unwrap(),
3580                source_dictionary: Default::default(),
3581            }),
3582            &b_component,
3583            &mut NoopRouteMapper,
3584        )
3585        .await
3586        .expect("failed to route runner");
3587
3588        // Verify this source comes from `a`.
3589        match source {
3590            RouteSource {
3591                source:
3592                    CapabilitySource::Component(ComponentSource {
3593                        capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3594                        moniker,
3595                    }),
3596                relative_path,
3597            } if relative_path.is_dot() => {
3598                assert_eq!(name, "elf");
3599                assert_eq!(
3600                    source_path.expect("missing source path"),
3601                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3602                        .parse::<cm_types::Path>()
3603                        .unwrap()
3604                );
3605                assert_eq!(&moniker, a_component.moniker());
3606            }
3607            _ => panic!("bad capability source"),
3608        };
3609    }
3610
3611    ///   a
3612    ///    \
3613    ///     b
3614    ///      \
3615    ///       c
3616    ///
3617    /// a: declares runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self.
3618    /// a: offers runner "elf" from self to "b" as "dwarf".
3619    /// b: registers runner "dwarf" from realm in environment as "hobbit".
3620    /// c: uses runner "hobbit".
3621    pub async fn test_route_runner_from_grandparent_environment(&self) {
3622        let components = vec![
3623            (
3624                "a",
3625                ComponentDeclBuilder::new()
3626                    .child_default("b")
3627                    .offer(
3628                        OfferBuilder::runner()
3629                            .name("elf")
3630                            .target_name("dwarf")
3631                            .source(OfferSource::Self_)
3632                            .target_static_child("b"),
3633                    )
3634                    .runner_default("elf")
3635                    .build(),
3636            ),
3637            (
3638                "b",
3639                ComponentDeclBuilder::new()
3640                    .child(ChildBuilder::new().name("c").environment("env"))
3641                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3642                        source_name: "dwarf".parse().unwrap(),
3643                        source: RegistrationSource::Parent,
3644                        target_name: "hobbit".parse().unwrap(),
3645                    }))
3646                    .build(),
3647            ),
3648            ("c", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3649        ];
3650
3651        let model = T::new("a", components).build().await;
3652        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3653        let c_component =
3654            model.look_up_instance(&["b", "c"].try_into().unwrap()).await.expect("c instance");
3655        let source = route_capability(
3656            RouteRequest::UseRunner(UseRunnerDecl {
3657                source: UseSource::Environment,
3658                source_name: "hobbit".parse().unwrap(),
3659                source_dictionary: Default::default(),
3660            }),
3661            &c_component,
3662            &mut NoopRouteMapper,
3663        )
3664        .await
3665        .expect("failed to route runner");
3666
3667        // Verify this source comes from `a`.
3668        match source {
3669            RouteSource {
3670                source:
3671                    CapabilitySource::Component(ComponentSource {
3672                        capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3673                        moniker,
3674                    }),
3675                relative_path,
3676            } if relative_path.is_dot() => {
3677                assert_eq!(name, "elf");
3678                assert_eq!(
3679                    source_path.expect("missing source path"),
3680                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3681                        .parse::<cm_types::Path>()
3682                        .unwrap()
3683                );
3684                assert_eq!(&moniker, a_component.moniker());
3685            }
3686            _ => panic!("bad capability source"),
3687        };
3688    }
3689
3690    ///   a
3691    ///  / \
3692    /// b   c
3693    ///
3694    /// a: registers runner "dwarf" from "b" in environment as "hobbit".
3695    /// b: exposes runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self as "dwarf".
3696    /// c: uses runner "hobbit".
3697    pub async fn test_route_runner_from_sibling_environment(&self) {
3698        let components = vec![
3699            (
3700                "a",
3701                ComponentDeclBuilder::new()
3702                    .child_default("b")
3703                    .child(ChildBuilder::new().name("c").environment("env"))
3704                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3705                        source_name: "dwarf".parse().unwrap(),
3706                        source: RegistrationSource::Child("b".parse().unwrap()),
3707                        target_name: "hobbit".parse().unwrap(),
3708                    }))
3709                    .build(),
3710            ),
3711            (
3712                "b",
3713                ComponentDeclBuilder::new()
3714                    .expose(
3715                        ExposeBuilder::runner()
3716                            .name("elf")
3717                            .target_name("dwarf")
3718                            .source(ExposeSource::Self_),
3719                    )
3720                    .runner_default("elf")
3721                    .build(),
3722            ),
3723            ("c", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3724        ];
3725
3726        let model = T::new("a", components).build().await;
3727        let b_component =
3728            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3729        let c_component =
3730            model.look_up_instance(&["c"].try_into().unwrap()).await.expect("c instance");
3731        let source = route_capability(
3732            RouteRequest::UseRunner(UseRunnerDecl {
3733                source: UseSource::Environment,
3734                source_name: "hobbit".parse().unwrap(),
3735                source_dictionary: Default::default(),
3736            }),
3737            &c_component,
3738            &mut NoopRouteMapper,
3739        )
3740        .await
3741        .expect("failed to route runner");
3742
3743        // Verify this source comes from `b`.
3744        match source {
3745            RouteSource {
3746                source:
3747                    CapabilitySource::Component(ComponentSource {
3748                        capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3749                        moniker,
3750                    }),
3751                relative_path,
3752            } if relative_path.is_dot() => {
3753                assert_eq!(name, "elf");
3754                assert_eq!(
3755                    source_path.expect("missing source path"),
3756                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3757                        .parse::<cm_types::Path>()
3758                        .unwrap()
3759                );
3760                assert_eq!(&moniker, b_component.moniker());
3761            }
3762            _ => panic!("bad capability source"),
3763        };
3764    }
3765
3766    ///   a
3767    ///    \
3768    ///     b
3769    ///      \
3770    ///       c
3771    ///
3772    /// a: declares runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self.
3773    /// a: registers runner "elf" from realm in environment as "hobbit".
3774    /// b: creates environment extending from realm.
3775    /// c: uses runner "hobbit".
3776    pub async fn test_route_runner_from_inherited_environment(&self) {
3777        let components = vec![
3778            (
3779                "a",
3780                ComponentDeclBuilder::new()
3781                    .child(ChildBuilder::new().name("b").environment("env"))
3782                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3783                        source_name: "elf".parse().unwrap(),
3784                        source: RegistrationSource::Self_,
3785                        target_name: "hobbit".parse().unwrap(),
3786                    }))
3787                    .runner_default("elf")
3788                    .build(),
3789            ),
3790            (
3791                "b",
3792                ComponentDeclBuilder::new()
3793                    .child(ChildBuilder::new().name("c").environment("env"))
3794                    .environment(EnvironmentBuilder::new().name("env"))
3795                    .build(),
3796            ),
3797            ("c", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3798        ];
3799
3800        let model = T::new("a", components).build().await;
3801        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3802        let c_component =
3803            model.look_up_instance(&["b", "c"].try_into().unwrap()).await.expect("c instance");
3804        let source = route_capability(
3805            RouteRequest::UseRunner(UseRunnerDecl {
3806                source: UseSource::Environment,
3807                source_name: "hobbit".parse().unwrap(),
3808                source_dictionary: Default::default(),
3809            }),
3810            &c_component,
3811            &mut NoopRouteMapper,
3812        )
3813        .await
3814        .expect("failed to route runner");
3815
3816        // Verify this source comes from `a`.
3817        match source {
3818            RouteSource {
3819                source:
3820                    CapabilitySource::Component(ComponentSource {
3821                        capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3822                        moniker,
3823                    }),
3824                relative_path,
3825            } if relative_path.is_dot() => {
3826                assert_eq!(name, "elf");
3827                assert_eq!(
3828                    source_path.expect("missing source path"),
3829                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3830                        .parse::<cm_types::Path>()
3831                        .unwrap()
3832                );
3833                assert_eq!(&moniker, a_component.moniker());
3834            }
3835            _ => panic!("bad capability source"),
3836        };
3837    }
3838
3839    ///  a
3840    ///   \
3841    ///    b
3842    ///
3843    /// a: declares runner "elf" with service format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from "self".
3844    /// a: registers runner "elf" from self in environment as "hobbit".
3845    /// b: uses runner "hobbit". Fails because "hobbit" was not in environment.
3846    pub async fn test_route_runner_from_environment_not_found(&self) {
3847        let components = vec![
3848            (
3849                "a",
3850                ComponentDeclBuilder::new()
3851                    .child(ChildBuilder::new().name("b").environment("env"))
3852                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3853                        source_name: "elf".parse().unwrap(),
3854                        source: RegistrationSource::Self_,
3855                        target_name: "dwarf".parse().unwrap(),
3856                    }))
3857                    .runner_default("elf")
3858                    .build(),
3859            ),
3860            ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3861        ];
3862
3863        let model = T::new("a", components).build().await;
3864        let b_component =
3865            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3866        let route_result = route_capability(
3867            RouteRequest::UseRunner(UseRunnerDecl {
3868                source: UseSource::Environment,
3869                source_name: "hobbit".parse().unwrap(),
3870                source_dictionary: Default::default(),
3871            }),
3872            &b_component,
3873            &mut NoopRouteMapper,
3874        )
3875        .await;
3876
3877        assert_matches!(
3878            route_result,
3879            Err(RoutingError::UseFromEnvironmentNotFound {
3880                    moniker,
3881                    capability_type,
3882                    capability_name,
3883                }
3884            )
3885                if moniker == *b_component.moniker() &&
3886                capability_type == "runner" &&
3887                capability_name == "hobbit"
3888        );
3889    }
3890
3891    ///   a
3892    ///    \
3893    ///     b
3894    ///
3895    /// a: registers built-in runner "elf" from realm in environment as "hobbit".
3896    /// b: uses runner "hobbit".
3897    pub async fn test_route_builtin_runner(&self) {
3898        let components = vec![
3899            (
3900                "a",
3901                ComponentDeclBuilder::new()
3902                    .child(ChildBuilder::new().name("b").environment("env"))
3903                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3904                        source_name: "elf".parse().unwrap(),
3905                        source: RegistrationSource::Parent,
3906                        target_name: "hobbit".parse().unwrap(),
3907                    }))
3908                    .build(),
3909            ),
3910            ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3911        ];
3912
3913        let mut builder = T::new("a", components);
3914        builder.set_builtin_capabilities(vec![CapabilityDecl::Runner(RunnerDecl {
3915            name: "elf".parse().unwrap(),
3916            source_path: None,
3917        })]);
3918        builder.register_mock_builtin_runner("elf");
3919        let model = builder.build().await;
3920
3921        let b_component =
3922            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
3923        let source = route_capability(
3924            RouteRequest::UseRunner(UseRunnerDecl {
3925                source: UseSource::Environment,
3926                source_name: "hobbit".parse().unwrap(),
3927                source_dictionary: Default::default(),
3928            }),
3929            &b_component,
3930            &mut NoopRouteMapper,
3931        )
3932        .await
3933        .expect("failed to route runner");
3934
3935        // Verify this is a built-in source.
3936        match source {
3937            RouteSource {
3938                source:
3939                    CapabilitySource::Builtin(BuiltinSource {
3940                        capability: InternalCapability::Runner(name),
3941                        ..
3942                    }),
3943                relative_path,
3944            } if relative_path.is_dot() => {
3945                assert_eq!(name, "elf");
3946            }
3947            _ => panic!("bad capability source"),
3948        };
3949    }
3950
3951    ///   a
3952    ///
3953    /// a: uses built-in runner "elf" from the root environment.
3954    pub async fn test_route_builtin_runner_from_root_env(&self) {
3955        let use_runner_decl =
3956            UseBuilder::runner().source(UseSource::Environment).name("elf").build();
3957        let components = vec![(
3958            "a",
3959            ComponentDeclBuilder::new_empty_component().use_(use_runner_decl.clone()).build(),
3960        )];
3961
3962        let mut builder = T::new("a", components);
3963        builder.set_builtin_capabilities(vec![CapabilityDecl::Runner(RunnerDecl {
3964            name: "elf".parse().unwrap(),
3965            source_path: None,
3966        })]);
3967        builder.register_mock_builtin_runner("elf");
3968        let model = builder.build().await;
3969
3970        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3971        let source = route_capability(
3972            RouteRequest::UseRunner(match use_runner_decl {
3973                UseDecl::Runner(u) => u,
3974                _ => panic!("unexpected use type"),
3975            }),
3976            &a_component,
3977            &mut NoopRouteMapper,
3978        )
3979        .await
3980        .expect("failed to route runner");
3981
3982        // Verify this is a built-in source.
3983        match source {
3984            RouteSource {
3985                source:
3986                    CapabilitySource::Builtin(BuiltinSource {
3987                        capability: InternalCapability::Runner(name),
3988                        ..
3989                    }),
3990                relative_path,
3991            } if relative_path.is_dot() => {
3992                assert_eq!(name, "elf");
3993            }
3994            _ => panic!("bad capability source"),
3995        };
3996    }
3997
3998    ///  a
3999    ///   \
4000    ///    b
4001    ///
4002    /// a: registers built-in runner "elf" from realm in environment as "hobbit". The ELF runner is
4003    ///    registered in the root environment, but not declared as a built-in capability.
4004    /// b: uses runner "hobbit"; should fail.
4005    pub async fn test_route_builtin_runner_not_found(&self) {
4006        let components = vec![
4007            (
4008                "a",
4009                ComponentDeclBuilder::new()
4010                    .child(ChildBuilder::new().name("b").environment("env"))
4011                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
4012                        source_name: "elf".parse().unwrap(),
4013                        source: RegistrationSource::Parent,
4014                        target_name: "hobbit".parse().unwrap(),
4015                    }))
4016                    .build(),
4017            ),
4018            ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
4019        ];
4020
4021        let mut builder = T::new("a", components);
4022        builder.register_mock_builtin_runner("elf");
4023
4024        let model = builder.build().await;
4025        let b_component =
4026            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
4027        let route_result = route_capability(
4028            RouteRequest::UseRunner(UseRunnerDecl {
4029                source: UseSource::Environment,
4030                source_name: "hobbit".parse().unwrap(),
4031                source_dictionary: Default::default(),
4032            }),
4033            &b_component,
4034            &mut NoopRouteMapper,
4035        )
4036        .await;
4037
4038        assert_matches!(
4039            route_result,
4040            Err(RoutingError::RegisterFromComponentManagerNotFound {
4041                    capability_id,
4042                }
4043            )
4044                if capability_id == "elf".to_string()
4045        );
4046    }
4047
4048    ///   a
4049    ///
4050    /// a: Attempts to use unregistered runner "hobbit" from the root environment.
4051    ///    The runner is provided as a built-in capability, but not registered in
4052    ///    the root environment.
4053    pub async fn test_route_builtin_runner_from_root_env_registration_not_found(&self) {
4054        let use_runner_decl = UseRunnerDecl {
4055            source: UseSource::Environment,
4056            source_name: "hobbit".parse().unwrap(),
4057            source_dictionary: Default::default(),
4058        };
4059        let components = vec![(
4060            "a",
4061            ComponentDeclBuilder::new_empty_component().use_(use_runner_decl.clone()).build(),
4062        )];
4063
4064        let builder = T::new("a", components);
4065        let model = builder.build().await;
4066
4067        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
4068        let route_result = route_capability(
4069            RouteRequest::UseRunner(use_runner_decl),
4070            &a_component,
4071            &mut NoopRouteMapper,
4072        )
4073        .await;
4074
4075        assert_matches!(
4076            route_result,
4077            Err(RoutingError::UseFromEnvironmentNotFound {
4078                    moniker,
4079                    capability_type,
4080                    capability_name,
4081                }
4082            )
4083                if moniker == *a_component.moniker()
4084                && capability_type == "runner".to_string()
4085                && capability_name == "hobbit"
4086        );
4087    }
4088
4089    ///  a
4090    ///   \
4091    ///    b
4092    ///
4093    /// a: uses runner "elf" from "#b" as "dwarf".
4094    /// b: exposes runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self as "dwarf".
4095    pub async fn test_use_runner_from_child(&self) {
4096        let components = vec![
4097            (
4098                "a",
4099                ComponentDeclBuilder::new_empty_component()
4100                    .use_(UseBuilder::runner().source_static_child("b").name("dwarf"))
4101                    .child_default("b")
4102                    .build(),
4103            ),
4104            (
4105                "b",
4106                ComponentDeclBuilder::new()
4107                    .expose(
4108                        ExposeBuilder::runner()
4109                            .name("elf")
4110                            .target_name("dwarf")
4111                            .source(ExposeSource::Self_),
4112                    )
4113                    .runner_default("elf")
4114                    .build(),
4115            ),
4116        ];
4117
4118        let model = T::new("a", components).build().await;
4119        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
4120        let b_component =
4121            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
4122        let source = route_capability(
4123            RouteRequest::UseRunner(UseRunnerDecl {
4124                source: UseSource::Child("b".parse().unwrap()),
4125                source_name: "dwarf".parse().unwrap(),
4126                source_dictionary: Default::default(),
4127            }),
4128            &a_component,
4129            &mut NoopRouteMapper,
4130        )
4131        .await
4132        .expect("failed to route runner");
4133
4134        // Verify this source comes from `b`.
4135        match source {
4136            RouteSource {
4137                source:
4138                    CapabilitySource::Component(ComponentSource {
4139                        capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
4140                        moniker,
4141                    }),
4142                relative_path,
4143            } if relative_path.is_dot() => {
4144                assert_eq!(name, "elf");
4145                assert_eq!(
4146                    source_path.expect("missing source path"),
4147                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
4148                        .parse::<cm_types::Path>()
4149                        .unwrap()
4150                );
4151                assert_eq!(&moniker, b_component.moniker());
4152            }
4153            _ => panic!("bad capability source"),
4154        };
4155    }
4156
4157    ///  a
4158    ///   \
4159    ///    b
4160    ///
4161    /// a: offers runner "elf" at path format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from self as "dwarf".
4162    /// b: uses runner "elf" from "parent" as "dwarf".
4163    pub async fn test_use_runner_from_parent(&self) {
4164        let components = vec![
4165            (
4166                "a",
4167                ComponentDeclBuilder::new()
4168                    .offer(
4169                        OfferBuilder::runner()
4170                            .name("elf")
4171                            .target_name("dwarf")
4172                            .source(OfferSource::Self_)
4173                            .target_static_child("b"),
4174                    )
4175                    .runner_default("elf")
4176                    .child_default("b")
4177                    .build(),
4178            ),
4179            (
4180                "b",
4181                ComponentDeclBuilder::new_empty_component()
4182                    .use_(UseBuilder::runner().name("dwarf"))
4183                    .build(),
4184            ),
4185        ];
4186
4187        let model = T::new("a", components).build().await;
4188        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
4189        let b_component =
4190            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
4191        let source = route_capability(
4192            RouteRequest::UseRunner(UseRunnerDecl {
4193                source: UseSource::Parent,
4194                source_name: "dwarf".parse().unwrap(),
4195                source_dictionary: Default::default(),
4196            }),
4197            &b_component,
4198            &mut NoopRouteMapper,
4199        )
4200        .await
4201        .expect("failed to route runner");
4202
4203        // Verify this source comes from `a`.
4204        match source {
4205            RouteSource {
4206                source:
4207                    CapabilitySource::Component(ComponentSource {
4208                        capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
4209                        moniker,
4210                    }),
4211                relative_path,
4212            } if relative_path.is_dot() => {
4213                assert_eq!(name, "elf");
4214                assert_eq!(
4215                    source_path.expect("missing source path"),
4216                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
4217                        .parse::<cm_types::Path>()
4218                        .unwrap()
4219                );
4220                assert_eq!(&moniker, a_component.moniker());
4221            }
4222            _ => panic!("bad capability source"),
4223        };
4224    }
4225
4226    ///  a
4227    ///   \
4228    ///    b
4229    ///
4230    /// a: declares runner "elf" with service format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME) from "self".
4231    /// a: registers runner "elf" from self in environment as "hobbit".
4232    /// b: uses runner "hobbit" from environment.
4233    pub async fn test_use_runner_from_parent_environment(&self) {
4234        let components = vec![
4235            (
4236                "a",
4237                ComponentDeclBuilder::new()
4238                    .child(ChildBuilder::new().name("b").environment("env"))
4239                    .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
4240                        source_name: "elf".parse().unwrap(),
4241                        source: RegistrationSource::Self_,
4242                        target_name: "hobbit".parse().unwrap(),
4243                    }))
4244                    .runner_default("elf")
4245                    .build(),
4246            ),
4247            (
4248                "b",
4249                ComponentDeclBuilder::new_empty_component()
4250                    .use_(UseBuilder::runner().source(UseSource::Environment).name("hobbit"))
4251                    .build(),
4252            ),
4253        ];
4254
4255        let model = T::new("a", components).build().await;
4256        let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
4257        let b_component =
4258            model.look_up_instance(&["b"].try_into().unwrap()).await.expect("b instance");
4259        let source = route_capability(
4260            RouteRequest::UseRunner(UseRunnerDecl {
4261                source: UseSource::Environment,
4262                source_name: "hobbit".parse().unwrap(),
4263                source_dictionary: Default::default(),
4264            }),
4265            &b_component,
4266            &mut NoopRouteMapper,
4267        )
4268        .await
4269        .expect("failed to route runner");
4270
4271        // Verify this source comes from `a`.
4272        match source {
4273            RouteSource {
4274                source:
4275                    CapabilitySource::Component(ComponentSource {
4276                        capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
4277                        moniker,
4278                    }),
4279                relative_path,
4280            } if relative_path.is_dot() => {
4281                assert_eq!(name, "elf");
4282                assert_eq!(
4283                    source_path.expect("missing source path"),
4284                    format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
4285                        .parse::<cm_types::Path>()
4286                        .unwrap()
4287                );
4288                assert_eq!(&moniker, a_component.moniker());
4289            }
4290            _ => panic!("bad capability source"),
4291        };
4292    }
4293
4294    ///  a
4295    ///   \
4296    ///    b
4297    ///
4298    /// b: declares "fuchsia.MyConfig" capability
4299    /// b: uses "fuchsia.MyConfig" from self
4300    pub async fn test_use_config_from_self(&self) {
4301        let good_value = cm_rust::ConfigSingleValue::Int8(12);
4302        let use_config = UseBuilder::config()
4303            .source(cm_rust::UseSource::Self_)
4304            .name("fuchsia.MyConfig")
4305            .target_name("my_config")
4306            .config_type(cm_rust::ConfigValueType::Int8)
4307            .build();
4308        let components = vec![
4309            ("a", ComponentDeclBuilder::new().child_default("b").build()),
4310            (
4311                "b",
4312                ComponentDeclBuilder::new()
4313                    .capability(
4314                        CapabilityBuilder::config()
4315                            .name("fuchsia.MyConfig")
4316                            .value(good_value.clone().into()),
4317                    )
4318                    .use_(use_config.clone())
4319                    .config(cm_rust::ConfigDecl {
4320                        fields: Box::from([cm_rust::ConfigField {
4321                            key: "my_config".into(),
4322                            type_: cm_rust::ConfigValueType::Int8,
4323                            mutability: Default::default(),
4324                        }]),
4325                        checksum: cm_rust::ConfigChecksum::Sha256([0; 32]),
4326                        value_source: cm_rust::ConfigValueSource::Capabilities(Default::default()),
4327                    })
4328                    .build(),
4329            ),
4330        ];
4331
4332        let model = T::new("a", components).build().await;
4333        let child_component = model.look_up_instance(&["b"].try_into().unwrap()).await.unwrap();
4334
4335        let cm_rust::UseDecl::Config(use_config) = use_config else { panic!() };
4336        let value =
4337            routing::config::route_config_value(&use_config, &child_component).await.unwrap();
4338        assert_eq!(value, Some(cm_rust::ConfigValue::Single(good_value)));
4339    }
4340
4341    ///  a
4342    ///   \
4343    ///    b
4344    ///
4345    /// a: declares "fuchsia.MyConfig" capability
4346    /// b: uses "fuchsia.MyConfig" from parent
4347    pub async fn test_use_config_from_parent(&self) {
4348        let good_value = cm_rust::ConfigSingleValue::Int8(12);
4349        let use_config = UseBuilder::config()
4350            .source(cm_rust::UseSource::Parent)
4351            .name("fuchsia.MyConfig")
4352            .target_name("my_config")
4353            .config_type(cm_rust::ConfigValueType::Int8)
4354            .build();
4355        let components = vec![
4356            (
4357                "a",
4358                ComponentDeclBuilder::new()
4359                    .capability(
4360                        CapabilityBuilder::config()
4361                            .name("fuchsia.MyConfig")
4362                            .value(good_value.clone().into()),
4363                    )
4364                    .offer(
4365                        OfferBuilder::config()
4366                            .name("fuchsia.MyConfig")
4367                            .source(cm_rust::OfferSource::Self_)
4368                            .target(cm_rust::OfferTarget::Child(cm_rust::ChildRef {
4369                                name: "b".parse().unwrap(),
4370                                collection: None,
4371                            })),
4372                    )
4373                    .child_default("b")
4374                    .build(),
4375            ),
4376            (
4377                "b",
4378                ComponentDeclBuilder::new()
4379                    .use_(use_config.clone())
4380                    .config(cm_rust::ConfigDecl {
4381                        fields: Box::from([cm_rust::ConfigField {
4382                            key: "my_config".into(),
4383                            type_: cm_rust::ConfigValueType::Int8,
4384                            mutability: Default::default(),
4385                        }]),
4386                        checksum: cm_rust::ConfigChecksum::Sha256([0; 32]),
4387                        value_source: cm_rust::ConfigValueSource::Capabilities(Default::default()),
4388                    })
4389                    .build(),
4390            ),
4391        ];
4392
4393        let model = T::new("a", components).build().await;
4394        let child_component = model.look_up_instance(&["b"].try_into().unwrap()).await.unwrap();
4395
4396        let cm_rust::UseDecl::Config(use_config) = use_config else { panic!() };
4397        let value =
4398            routing::config::route_config_value(&use_config, &child_component).await.unwrap();
4399        assert_eq!(value, Some(cm_rust::ConfigValue::Single(good_value)));
4400    }
4401
4402    ///  a
4403    ///   \
4404    ///    b
4405    ///
4406    /// a: routes "fuchsia.MyConfig" from void
4407    /// b: uses "fuchsia.MyConfig" from parent
4408    pub async fn test_use_config_from_void(&self) {
4409        let mut use_config = UseBuilder::config()
4410            .source(cm_rust::UseSource::Parent)
4411            .name("fuchsia.MyConfig")
4412            .target_name("my_config")
4413            .availability(cm_rust::Availability::Optional)
4414            .config_type(cm_rust::ConfigValueType::Int8)
4415            .build();
4416        match &mut use_config {
4417            cm_rust::UseDecl::Config(use_config_decl) => {
4418                use_config_decl.default =
4419                    Some(cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Int8(0)))
4420            }
4421            _ => panic!("unexpected use declaration variant"),
4422        }
4423        let components = vec![
4424            (
4425                "a",
4426                ComponentDeclBuilder::new()
4427                    .offer(
4428                        OfferBuilder::config()
4429                            .name("fuchsia.MyConfig")
4430                            .source(cm_rust::OfferSource::Void)
4431                            .availability(cm_rust::Availability::Optional)
4432                            .target(cm_rust::OfferTarget::Child(cm_rust::ChildRef {
4433                                name: "b".parse().unwrap(),
4434                                collection: None,
4435                            })),
4436                    )
4437                    .child_default("b")
4438                    .build(),
4439            ),
4440            (
4441                "b",
4442                ComponentDeclBuilder::new()
4443                    .use_(use_config.clone())
4444                    .config(cm_rust::ConfigDecl {
4445                        fields: Box::from([cm_rust::ConfigField {
4446                            key: "my_config".into(),
4447                            type_: cm_rust::ConfigValueType::Int8,
4448                            mutability: Default::default(),
4449                        }]),
4450                        checksum: cm_rust::ConfigChecksum::Sha256([0; 32]),
4451                        value_source: cm_rust::ConfigValueSource::Capabilities(Default::default()),
4452                    })
4453                    .build(),
4454            ),
4455        ];
4456
4457        let model = T::new("a", components).build().await;
4458        let child_component = model.look_up_instance(&["b"].try_into().unwrap()).await.unwrap();
4459
4460        let cm_rust::UseDecl::Config(use_config) = use_config else { panic!() };
4461        let value =
4462            routing::config::route_config_value(&use_config, &child_component).await.unwrap();
4463        assert_eq!(value, Some(cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Int8(0))));
4464    }
4465
4466    pub async fn test_use_dictionary_protocol_from_self(&self) {
4467        let components = vec![(
4468            "root",
4469            ComponentDeclBuilder::new()
4470                .dictionary_default("my_dict")
4471                .use_(
4472                    UseBuilder::protocol()
4473                        .source(UseSource::Self_)
4474                        .name("A")
4475                        .path("/svc/B")
4476                        .from_dictionary("my_dict"),
4477                )
4478                .build(),
4479        )];
4480
4481        let model = T::new("root", components).build().await;
4482        let root_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
4483
4484        let route_result = route_capability(
4485            RouteRequest::UseProtocol(UseProtocolDecl {
4486                source: UseSource::Self_,
4487                source_name: "A".parse().unwrap(),
4488                source_dictionary: "my_dict".parse().unwrap(),
4489                target_path: Some("/svc/B".parse().unwrap()),
4490                #[cfg(fuchsia_api_level_at_least = "29")]
4491                numbered_handle: None,
4492                dependency_type: DependencyType::Strong,
4493                availability: Availability::Required,
4494            }),
4495            &root_component,
4496            &mut NoopRouteMapper,
4497        )
4498        .await;
4499
4500        assert_matches!(
4501            route_result,
4502            Err(RoutingError::UseFromSelfNotFound { moniker, capability_id })
4503                if capability_id == "my_dict/A" && moniker == Moniker::root()
4504        );
4505    }
4506
4507    pub async fn test_offer_dictionary_to_grandchild_not_supported(&self) {
4508        // Using a dictionary in legacy routing isn't supported. Also, when a
4509        // component uses a protocol from its grandparent that passes through a
4510        // dictionary, that should trickle down into a DictionariesNotSupported
4511        // rather than another type of routing error.
4512        let components = vec![
4513            (
4514                "root",
4515                ComponentDeclBuilder::new()
4516                    // It doesn't matter that this dictionary doesn't
4517                    // actually contain the required protocol, since routing
4518                    // will fail before that.
4519                    .dictionary_default("parent_dict")
4520                    .offer(
4521                        OfferBuilder::protocol()
4522                            .name("A")
4523                            .from_dictionary("parent_dict")
4524                            .source(OfferSource::Self_)
4525                            .target_static_child("intermediate"),
4526                    )
4527                    .child_default("intermediate")
4528                    .build(),
4529            ),
4530            (
4531                "intermediate",
4532                ComponentDeclBuilder::new()
4533                    .offer(
4534                        OfferBuilder::protocol()
4535                            .source(OfferSource::Parent)
4536                            .name("A")
4537                            .target_static_child("leaf"),
4538                    )
4539                    .child_default("leaf")
4540                    .build(),
4541            ),
4542            (
4543                "leaf",
4544                ComponentDeclBuilder::new()
4545                    .use_(UseBuilder::protocol().source(UseSource::Parent).name("A"))
4546                    .build(),
4547            ),
4548        ];
4549
4550        let model = T::new("root", components).build().await;
4551        let leaf_component = model
4552            .look_up_instance(&["intermediate", "leaf"].try_into().unwrap())
4553            .await
4554            .expect("leaf instance");
4555
4556        let route_result = route_capability(
4557            RouteRequest::UseProtocol(UseProtocolDecl {
4558                source: UseSource::Parent,
4559                source_name: "A".parse().unwrap(),
4560                source_dictionary: Default::default(),
4561                target_path: Some("/svc/dict_protocol".parse().unwrap()),
4562                #[cfg(fuchsia_api_level_at_least = "29")]
4563                numbered_handle: None,
4564                dependency_type: DependencyType::Strong,
4565                availability: Availability::Required,
4566            }),
4567            &leaf_component,
4568            &mut NoopRouteMapper,
4569        )
4570        .await;
4571
4572        assert_matches!(
4573            route_result,
4574            Err(RoutingError::BedrockNotPresentInDictionary { name, .. })
4575                if name == "svc/dict_protocol"
4576        );
4577    }
4578
4579    pub async fn test_expose_dictionary_to_grandparent_not_supported(&self) {
4580        // Same as above: using a dictionary in legacy routing isn't supported,
4581        // but check the expose direction.
4582        let components = vec![
4583            (
4584                "root",
4585                ComponentDeclBuilder::new()
4586                    .use_(
4587                        UseBuilder::protocol()
4588                            .source(UseSource::Child("intermediate".parse().unwrap()))
4589                            .name("A"),
4590                    )
4591                    .child_default("intermediate")
4592                    .build(),
4593            ),
4594            (
4595                "intermediate",
4596                ComponentDeclBuilder::new()
4597                    .expose(
4598                        ExposeBuilder::protocol()
4599                            .source(ExposeSource::Child("leaf".parse().unwrap()))
4600                            .name("A")
4601                            .target(ExposeTarget::Parent),
4602                    )
4603                    .child_default("leaf")
4604                    .build(),
4605            ),
4606            (
4607                "leaf",
4608                ComponentDeclBuilder::new()
4609                    .dictionary_default("child_dict")
4610                    .expose(
4611                        ExposeBuilder::protocol()
4612                            .name("A")
4613                            .from_dictionary("child_dict")
4614                            .source(ExposeSource::Self_)
4615                            .target(ExposeTarget::Parent),
4616                    )
4617                    .build(),
4618            ),
4619        ];
4620
4621        let model = T::new("root", components).build().await;
4622        let root_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
4623        let route_result = route_capability(
4624            RouteRequest::UseProtocol(UseProtocolDecl {
4625                source: UseSource::Child("intermediate".parse().unwrap()),
4626                source_name: "A".parse().unwrap(),
4627                source_dictionary: Default::default(),
4628                target_path: Some("/svc/dict_protocol".parse().unwrap()),
4629                #[cfg(fuchsia_api_level_at_least = "29")]
4630                numbered_handle: None,
4631                dependency_type: DependencyType::Strong,
4632                availability: Availability::Required,
4633            }),
4634            &root_component,
4635            &mut NoopRouteMapper,
4636        )
4637        .await;
4638
4639        assert_matches!(
4640            route_result,
4641            Err(RoutingError::BedrockNotPresentInDictionary { name, .. })
4642                if name == "svc/dict_protocol"
4643        );
4644    }
4645}