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