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