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