1pub mod availability;
6pub mod component_id_index;
7pub mod policy;
8pub mod rights;
9pub mod storage;
10pub mod storage_admin;
11
12use ::component_id_index::InstanceId;
13use assert_matches::assert_matches;
14use async_trait::async_trait;
15use camino::Utf8PathBuf;
16use cm_config::{
17 AllowlistEntry, AllowlistEntryBuilder, CapabilityAllowlistKey, CapabilityAllowlistSource,
18 DebugCapabilityAllowlistEntry, DebugCapabilityKey,
19};
20use cm_rust::*;
21use cm_rust_testing::*;
22use cm_types::Name;
23use fidl::endpoints::ProtocolMarker;
24use moniker::{ExtendedMoniker, Moniker};
25use routing::capability_source::{
26 AggregateCapability, AggregateMember, AnonymizedAggregateSource, BuiltinSource,
27 CapabilitySource, ComponentCapability, ComponentSource, FilteredAggregateProviderSource,
28 InternalCapability,
29};
30use routing::component_instance::ComponentInstanceInterface;
31use routing::error::RoutingError;
32use routing::mapper::NoopRouteMapper;
33use routing::{route_capability, RouteRequest, RouteSource};
34use std::collections::HashSet;
35use std::marker::PhantomData;
36use std::path::{Path, PathBuf};
37use std::sync::Arc;
38use {
39 fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_runner as fcrunner,
40 fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio, zx_status as zx,
41};
42
43pub fn default_service_capability() -> cm_types::Path {
45 "/svc/hippo".parse().unwrap()
46}
47
48pub fn default_directory_capability() -> cm_types::Path {
50 "/data/hippo".parse().unwrap()
51}
52
53pub fn default_component_decl() -> ComponentDecl {
55 ComponentDecl::default()
56}
57
58pub fn component_decl_with_test_runner() -> ComponentDecl {
61 ComponentDecl {
62 program: Some(ProgramDecl {
63 runner: Some(TEST_RUNNER_NAME.parse().unwrap()),
64 info: fdata::Dictionary { entries: Some(vec![]), ..Default::default() },
65 }),
66 ..Default::default()
67 }
68}
69
70pub fn component_decl_with_exposed_binder() -> ComponentDecl {
72 ComponentDecl {
73 program: Some(ProgramDecl {
74 runner: Some(TEST_RUNNER_NAME.parse().unwrap()),
75 info: fdata::Dictionary { entries: Some(vec![]), ..Default::default() },
76 }),
77 exposes: vec![ExposeBuilder::protocol()
78 .source(ExposeSource::Framework)
79 .name(fcomponent::BinderMarker::DEBUG_NAME)
80 .build()],
81 ..Default::default()
82 }
83}
84
85#[derive(Debug, PartialEq, Clone)]
86pub enum ExpectedResult {
87 Ok,
88 Err(zx::Status),
89 ErrWithNoEpitaph,
90}
91
92#[derive(Debug, Clone, Eq, PartialEq)]
93pub struct ComponentEventRoute {
94 pub component: String,
96 pub scope: Option<Vec<String>>,
101}
102
103#[derive(Debug)]
104pub enum ServiceInstance {
105 Named(String),
106 Aggregated(usize),
107}
108
109#[derive(Debug)]
110pub enum CheckUse {
111 Protocol {
112 path: cm_types::Path,
113 expected_res: ExpectedResult,
114 },
115 Service {
116 path: cm_types::Path,
117 instance: ServiceInstance,
118 member: String,
119 expected_res: ExpectedResult,
120 },
121 Directory {
122 path: cm_types::Path,
123 file: PathBuf,
124 expected_res: ExpectedResult,
125 },
126 Storage {
127 path: cm_types::Path,
128 storage_relation: Option<Moniker>,
131 from_cm_namespace: bool,
134 storage_subdir: Option<String>,
135 expected_res: ExpectedResult,
136 },
137 StorageAdmin {
138 storage_relation: Moniker,
140 storage_subdir: Option<String>,
141 expected_res: ExpectedResult,
142 },
143 EventStream {
144 path: cm_types::Path,
145 scope: Vec<ComponentEventRoute>,
146 name: Name,
147 expected_res: ExpectedResult,
148 },
149}
150
151impl CheckUse {
152 pub fn default_directory(expected_res: ExpectedResult) -> Self {
153 Self::Directory {
154 path: default_directory_capability(),
155 file: PathBuf::from("hippo"),
156 expected_res,
157 }
158 }
159}
160
161pub fn generate_storage_path(
163 subdir: Option<String>,
164 moniker: &Moniker,
165 instance_id: Option<&InstanceId>,
166) -> PathBuf {
167 if let Some(id) = instance_id {
168 return id.to_string().into();
169 }
170 let mut path = moniker.path().iter();
171 let mut dir_path = vec![];
172 if let Some(subdir) = subdir {
173 dir_path.push(subdir);
174 }
175 if let Some(p) = path.next() {
176 dir_path.push(format!("{p}:0"));
177 }
178 while let Some(p) = path.next() {
179 dir_path.push("children".to_string());
180 dir_path.push(format!("{p}:0"));
181 }
182
183 dir_path.push("data".to_string());
188 dir_path.into_iter().collect()
189}
190
191#[async_trait]
194pub trait RoutingTestModel {
195 type C: ComponentInstanceInterface + std::fmt::Debug + 'static;
196
197 async fn check_use(&self, moniker: Moniker, check: CheckUse);
199
200 async fn check_use_exposed_dir(&self, moniker: Moniker, check: CheckUse);
202
203 async fn check_exposed_to_framework(&self, moniker: Moniker, check: CheckUse);
208
209 async fn look_up_instance(&self, moniker: &Moniker) -> Result<Arc<Self::C>, anyhow::Error>;
211
212 async fn check_open_node(&self, moniker: Moniker, path: cm_types::Path);
215
216 async fn create_static_file(&self, path: &Path, contents: &str) -> Result<(), anyhow::Error>;
219
220 fn install_namespace_directory(&self, path: &str);
222
223 fn add_subdir_to_data_directory(&self, subdir: &str);
225
226 async fn check_test_subdir_contents(&self, path: &str, expected: Vec<String>);
229
230 async fn check_namespace_subdir_contents(&self, path: &str, expected: Vec<String>);
232
233 async fn check_test_subdir_contains(&self, path: &str, expected: String);
235
236 async fn check_test_dir_tree_contains(&self, expected: String);
238}
239
240#[async_trait]
242pub trait RoutingTestModelBuilder {
243 type Model: RoutingTestModel;
244
245 fn new(root_component: &str, components: Vec<(&'static str, ComponentDecl)>) -> Self;
248
249 fn set_namespace_capabilities(&mut self, caps: Vec<CapabilityDecl>);
251
252 fn set_builtin_capabilities(&mut self, caps: Vec<CapabilityDecl>);
254
255 fn register_mock_builtin_runner(&mut self, runner: &str);
257
258 fn add_capability_policy(
260 &mut self,
261 key: CapabilityAllowlistKey,
262 allowlist: HashSet<AllowlistEntry>,
263 );
264
265 fn add_debug_capability_policy(
267 &mut self,
268 key: DebugCapabilityKey,
269 allowlist: HashSet<DebugCapabilityAllowlistEntry>,
270 );
271
272 fn set_component_id_index_path(&mut self, path: Utf8PathBuf);
274
275 async fn build(self) -> Self::Model;
276}
277
278#[macro_export]
282macro_rules! instantiate_common_routing_tests {
283 ($builder_impl:path) => {
284 instantiate_common_routing_tests! {
286 $builder_impl,
287 test_use_from_parent,
288 test_use_from_child,
289 test_use_from_self,
290 test_use_from_grandchild,
291 test_use_from_grandparent,
292 test_use_from_sibling_no_root,
293 test_use_from_sibling_root,
294 test_use_from_niece,
295 test_use_kitchen_sink,
296 test_use_from_component_manager_namespace,
297 test_offer_from_component_manager_namespace,
298 test_use_not_offered,
299 test_use_offer_source_not_exposed,
300 test_use_offer_source_not_offered,
301 test_use_from_expose,
302 test_route_protocol_from_expose,
303 test_use_from_expose_to_framework,
304 test_offer_from_non_executable,
305 test_route_filtered_aggregate_service,
306 test_route_anonymized_aggregate_service,
307 test_use_directory_with_subdir_from_grandparent,
308 test_use_directory_with_subdir_from_sibling,
309 test_expose_directory_with_subdir,
310 test_expose_from_self_and_child,
311 test_use_not_exposed,
312 test_expose_to_framework_from_self,
313 test_expose_to_framework_from_child,
314 test_use_protocol_denied_by_capability_policy,
315 test_use_directory_with_alias_denied_by_capability_policy,
316 test_use_protocol_partial_chain_allowed_by_capability_policy,
317 test_use_protocol_component_provided_capability_policy,
318 test_use_from_component_manager_namespace_denied_by_policy,
319 test_event_stream_aliasing,
320 test_use_event_stream_from_above_root,
321 test_use_event_stream_from_above_root_and_downscoped,
322 test_can_offer_capability_requested_event,
323 test_route_service_from_parent,
324 test_route_service_from_child,
325 test_route_service_from_sibling,
326 test_route_filtered_service_from_sibling,
327 test_route_renamed_service_instance_from_sibling,
328 test_use_builtin_from_grandparent,
329 test_invalid_use_from_component_manager,
330 test_invalid_offer_from_component_manager,
331 test_route_runner_from_parent_environment,
332 test_route_runner_from_grandparent_environment,
333 test_route_runner_from_sibling_environment,
334 test_route_runner_from_inherited_environment,
335 test_route_runner_from_environment_not_found,
336 test_route_builtin_runner,
337 test_route_builtin_runner_from_root_env,
338 test_route_builtin_runner_not_found,
339 test_route_builtin_runner_from_root_env_registration_not_found,
340 test_use_runner_from_child,
341 test_use_runner_from_parent,
342 test_use_runner_from_parent_environment,
343 test_use_config_from_self,
344 test_use_config_from_parent,
345 test_use_config_from_void,
346 test_use_dictionary_protocol_from_self,
347 test_offer_dictionary_to_grandchild_not_supported,
348 test_expose_dictionary_to_grandparent_not_supported,
349 }
350 };
351 ($builder_impl:path, $test:ident, $($remaining:ident),+ $(,)?) => {
352 instantiate_common_routing_tests! { $builder_impl, $test }
353 instantiate_common_routing_tests! { $builder_impl, $($remaining),+ }
354 };
355 ($builder_impl:path, $test:ident) => {
356 #[fuchsia_async::run_singlethreaded(test)]
359 async fn $test() {
360 fuchsia::init_logging_for_component_with_executor(
361 || {}, fuchsia::LoggingOptions::default())();
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(
435 vec!["b"].try_into().unwrap(),
436 CheckUse::default_directory(ExpectedResult::Ok),
437 )
438 .await;
439 model
440 .check_use(
441 vec!["b"].try_into().unwrap(),
442 CheckUse::Protocol {
443 path: default_service_capability(),
444 expected_res: ExpectedResult::Ok,
445 },
446 )
447 .await;
448 model.check_open_node(vec!["b"].try_into().unwrap(), "/svc/device".parse().unwrap()).await;
449 }
450
451 pub async fn test_use_from_child(&self) {
460 let components = vec![
461 (
462 "a",
463 ComponentDeclBuilder::new()
464 .use_(
465 UseBuilder::directory()
466 .source_static_child("b")
467 .name("bar_data")
468 .path("/data/hippo"),
469 )
470 .use_(
471 UseBuilder::protocol()
472 .source_static_child("b")
473 .name("bar")
474 .path("/svc/hippo"),
475 )
476 .child_default("b")
477 .build(),
478 ),
479 (
480 "b",
481 ComponentDeclBuilder::new()
482 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
483 .protocol_default("foo")
484 .expose(
485 ExposeBuilder::directory()
486 .name("foo_data")
487 .source(ExposeSource::Self_)
488 .target_name("bar_data")
489 .rights(fio::R_STAR_DIR),
490 )
491 .expose(
492 ExposeBuilder::protocol()
493 .name("foo")
494 .target_name("bar")
495 .source(ExposeSource::Self_),
496 )
497 .build(),
498 ),
499 ];
500 let model = T::new("a", components).build().await;
501 model.check_use(Moniker::root(), CheckUse::default_directory(ExpectedResult::Ok)).await;
502 model
503 .check_use(
504 Moniker::root(),
505 CheckUse::Protocol {
506 path: default_service_capability(),
507 expected_res: ExpectedResult::Ok,
508 },
509 )
510 .await;
511 }
512
513 pub async fn test_use_from_self(&self) {
515 let components = vec![(
516 "a",
517 ComponentDeclBuilder::new()
518 .protocol_default("hippo")
519 .use_(UseBuilder::protocol().source(UseSource::Self_).name("hippo"))
520 .build(),
521 )];
522 let model = T::new("a", components).build().await;
523 model
524 .check_use(
525 Moniker::root(),
526 CheckUse::Protocol {
527 path: default_service_capability(),
528 expected_res: ExpectedResult::Ok,
529 },
530 )
531 .await;
532 }
533
534 pub async fn test_use_from_grandchild(&self) {
547 let components = vec![
548 (
549 "a",
550 ComponentDeclBuilder::new()
551 .use_(
552 UseBuilder::directory()
553 .source_static_child("b")
554 .name("baz_data")
555 .path("/data/hippo"),
556 )
557 .use_(
558 UseBuilder::protocol()
559 .source_static_child("b")
560 .name("baz")
561 .path("/svc/hippo"),
562 )
563 .child_default("b")
564 .build(),
565 ),
566 (
567 "b",
568 ComponentDeclBuilder::new()
569 .expose(
570 ExposeBuilder::directory()
571 .name("bar_data")
572 .source_static_child("c")
573 .target_name("baz_data")
574 .rights(fio::R_STAR_DIR),
575 )
576 .expose(
577 ExposeBuilder::protocol()
578 .name("bar")
579 .target_name("baz")
580 .source_static_child("c"),
581 )
582 .child_default("c")
583 .build(),
584 ),
585 (
586 "c",
587 ComponentDeclBuilder::new()
588 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
589 .protocol_default("foo")
590 .expose(
591 ExposeBuilder::directory()
592 .name("foo_data")
593 .source(ExposeSource::Self_)
594 .target_name("bar_data")
595 .rights(fio::R_STAR_DIR),
596 )
597 .expose(
598 ExposeBuilder::protocol()
599 .name("foo")
600 .target_name("bar")
601 .source(ExposeSource::Self_),
602 )
603 .build(),
604 ),
605 ];
606 let model = T::new("a", components).build().await;
607 model.check_use(Moniker::root(), CheckUse::default_directory(ExpectedResult::Ok)).await;
608 model
609 .check_use(
610 Moniker::root(),
611 CheckUse::Protocol {
612 path: default_service_capability(),
613 expected_res: ExpectedResult::Ok,
614 },
615 )
616 .await;
617 }
618
619 pub async fn test_use_from_grandparent(&self) {
632 let components = vec![
633 (
634 "a",
635 ComponentDeclBuilder::new()
636 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
637 .protocol_default("foo")
638 .offer(
639 OfferBuilder::directory()
640 .name("foo_data")
641 .target_name("bar_data")
642 .source(OfferSource::Self_)
643 .target_static_child("b")
644 .rights(fio::R_STAR_DIR),
645 )
646 .offer(
647 OfferBuilder::protocol()
648 .name("foo")
649 .target_name("bar")
650 .source(OfferSource::Self_)
651 .target_static_child("b"),
652 )
653 .child_default("b")
654 .build(),
655 ),
656 (
657 "b",
658 ComponentDeclBuilder::new()
659 .offer(
660 OfferBuilder::directory()
661 .name("bar_data")
662 .target_name("baz_data")
663 .source(OfferSource::Parent)
664 .target_static_child("c")
665 .rights(fio::R_STAR_DIR),
666 )
667 .offer(
668 OfferBuilder::protocol()
669 .name("bar")
670 .target_name("baz")
671 .source(OfferSource::Parent)
672 .target_static_child("c"),
673 )
674 .child_default("c")
675 .build(),
676 ),
677 (
678 "c",
679 ComponentDeclBuilder::new()
680 .use_(UseBuilder::directory().name("baz_data").path("/data/hippo"))
681 .use_(UseBuilder::protocol().name("baz").path("/svc/hippo"))
682 .build(),
683 ),
684 ];
685 let model = T::new("a", components).build().await;
686 model
687 .check_use(
688 vec!["b", "c"].try_into().unwrap(),
689 CheckUse::default_directory(ExpectedResult::Ok),
690 )
691 .await;
692 model
693 .check_use(
694 vec!["b", "c"].try_into().unwrap(),
695 CheckUse::Protocol {
696 path: default_service_capability(),
697 expected_res: ExpectedResult::Ok,
698 },
699 )
700 .await;
701 }
702
703 pub async fn test_use_builtin_from_grandparent(&self) {
713 let components = vec![
714 (
715 "a",
716 ComponentDeclBuilder::new()
717 .offer(
718 OfferBuilder::protocol()
719 .name("builtin.Echo")
720 .source(OfferSource::Parent)
721 .target_static_child("b"),
722 )
723 .child_default("b")
724 .build(),
725 ),
726 (
727 "b",
728 ComponentDeclBuilder::new()
729 .offer(
730 OfferBuilder::protocol()
731 .name("builtin.Echo")
732 .source(OfferSource::Parent)
733 .target_static_child("c"),
734 )
735 .child_default("c")
736 .build(),
737 ),
738 (
739 "c",
740 ComponentDeclBuilder::new()
741 .use_(UseBuilder::protocol().name("builtin.Echo").path("/svc/hippo"))
742 .build(),
743 ),
744 ];
745
746 let mut builder = T::new("a", components);
747 builder.set_builtin_capabilities(vec![CapabilityDecl::Protocol(ProtocolDecl {
748 name: "builtin.Echo".parse().unwrap(),
749 source_path: None,
750 delivery: Default::default(),
751 })]);
752 let model = builder.build().await;
753
754 model
755 .check_use(
756 vec!["b", "c"].try_into().unwrap(),
757 CheckUse::Protocol {
758 path: default_service_capability(),
759 expected_res: ExpectedResult::Ok,
760 },
761 )
762 .await;
763 }
764
765 pub async fn test_use_from_sibling_no_root(&self) {
775 let components = vec![
776 ("a", ComponentDeclBuilder::new().child_default("b").build()),
777 (
778 "b",
779 ComponentDeclBuilder::new()
780 .offer(
781 OfferBuilder::directory()
782 .name("bar_data")
783 .target_name("foobar_data")
784 .source_static_child("d")
785 .target_static_child("c")
786 .rights(fio::R_STAR_DIR),
787 )
788 .offer(
789 OfferBuilder::protocol()
790 .name("bar")
791 .target_name("foobar")
792 .source_static_child("d")
793 .target_static_child("c"),
794 )
795 .child_default("c")
796 .child_default("d")
797 .build(),
798 ),
799 (
800 "c",
801 ComponentDeclBuilder::new()
802 .use_(UseBuilder::directory().name("foobar_data").path("/data/hippo"))
803 .use_(UseBuilder::protocol().name("foobar").path("/svc/hippo"))
804 .build(),
805 ),
806 (
807 "d",
808 ComponentDeclBuilder::new()
809 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
810 .protocol_default("foo")
811 .expose(
812 ExposeBuilder::directory()
813 .name("foo_data")
814 .source(ExposeSource::Self_)
815 .target_name("bar_data")
816 .rights(fio::R_STAR_DIR),
817 )
818 .expose(
819 ExposeBuilder::protocol()
820 .name("foo")
821 .target_name("bar")
822 .source(ExposeSource::Self_),
823 )
824 .build(),
825 ),
826 ];
827 let model = T::new("a", components).build().await;
828 model
829 .check_use(
830 vec!["b", "c"].try_into().unwrap(),
831 CheckUse::default_directory(ExpectedResult::Ok),
832 )
833 .await;
834 model
835 .check_use(
836 vec!["b", "c"].try_into().unwrap(),
837 CheckUse::Protocol {
838 path: default_service_capability(),
839 expected_res: ExpectedResult::Ok,
840 },
841 )
842 .await;
843 }
844
845 pub async fn test_use_from_sibling_root(&self) {
853 let components = vec![
854 (
855 "a",
856 ComponentDeclBuilder::new()
857 .offer(
858 OfferBuilder::directory()
859 .name("bar_data")
860 .target_name("baz_data")
861 .source_static_child("b")
862 .target_static_child("c")
863 .rights(fio::R_STAR_DIR),
864 )
865 .offer(
866 OfferBuilder::protocol()
867 .name("bar")
868 .target_name("baz")
869 .source_static_child("b")
870 .target_static_child("c"),
871 )
872 .child_default("b")
873 .child_default("c")
874 .build(),
875 ),
876 (
877 "b",
878 ComponentDeclBuilder::new()
879 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
880 .protocol_default("foo")
881 .expose(
882 ExposeBuilder::directory()
883 .name("foo_data")
884 .source(ExposeSource::Self_)
885 .target_name("bar_data")
886 .rights(fio::R_STAR_DIR),
887 )
888 .expose(
889 ExposeBuilder::protocol()
890 .name("foo")
891 .target_name("bar")
892 .source(ExposeSource::Self_),
893 )
894 .build(),
895 ),
896 (
897 "c",
898 ComponentDeclBuilder::new()
899 .use_(UseBuilder::directory().name("baz_data").path("/data/hippo"))
900 .use_(UseBuilder::protocol().name("baz").path("/svc/hippo"))
901 .build(),
902 ),
903 ];
904 let model = T::new("a", components).build().await;
905 model
906 .check_use(
907 vec!["c"].try_into().unwrap(),
908 CheckUse::default_directory(ExpectedResult::Ok),
909 )
910 .await;
911 model
912 .check_use(
913 vec!["c"].try_into().unwrap(),
914 CheckUse::Protocol {
915 path: default_service_capability(),
916 expected_res: ExpectedResult::Ok,
917 },
918 )
919 .await;
920 }
921
922 pub async fn test_use_from_niece(&self) {
933 let components = vec![
934 (
935 "a",
936 ComponentDeclBuilder::new()
937 .offer(
938 OfferBuilder::directory()
939 .name("baz_data")
940 .target_name("foobar_data")
941 .source_static_child("b")
942 .target_static_child("c")
943 .rights(fio::R_STAR_DIR),
944 )
945 .offer(
946 OfferBuilder::protocol()
947 .name("baz")
948 .target_name("foobar")
949 .source_static_child("b")
950 .target_static_child("c"),
951 )
952 .child_default("b")
953 .child_default("c")
954 .build(),
955 ),
956 (
957 "b",
958 ComponentDeclBuilder::new()
959 .expose(
960 ExposeBuilder::directory()
961 .name("bar_data")
962 .source_static_child("d")
963 .target_name("baz_data")
964 .rights(fio::R_STAR_DIR),
965 )
966 .expose(
967 ExposeBuilder::protocol()
968 .name("bar")
969 .target_name("baz")
970 .source_static_child("d"),
971 )
972 .child_default("d")
973 .build(),
974 ),
975 (
976 "c",
977 ComponentDeclBuilder::new()
978 .use_(UseBuilder::directory().name("foobar_data").path("/data/hippo"))
979 .use_(UseBuilder::protocol().name("foobar").path("/svc/hippo"))
980 .build(),
981 ),
982 (
983 "d",
984 ComponentDeclBuilder::new()
985 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
986 .protocol_default("foo")
987 .expose(
988 ExposeBuilder::directory()
989 .name("foo_data")
990 .source(ExposeSource::Self_)
991 .target_name("bar_data")
992 .rights(fio::R_STAR_DIR),
993 )
994 .expose(
995 ExposeBuilder::protocol()
996 .name("foo")
997 .target_name("bar")
998 .source(ExposeSource::Self_),
999 )
1000 .build(),
1001 ),
1002 ];
1003 let model = T::new("a", components).build().await;
1004 model
1005 .check_use(
1006 vec!["c"].try_into().unwrap(),
1007 CheckUse::default_directory(ExpectedResult::Ok),
1008 )
1009 .await;
1010 model
1011 .check_use(
1012 vec!["c"].try_into().unwrap(),
1013 CheckUse::Protocol {
1014 path: default_service_capability(),
1015 expected_res: ExpectedResult::Ok,
1016 },
1017 )
1018 .await;
1019 }
1020
1021 pub async fn test_use_kitchen_sink(&self) {
1034 let components = vec![
1035 (
1036 "a",
1037 ComponentDeclBuilder::new()
1038 .protocol_default("foo")
1039 .offer(
1040 OfferBuilder::protocol()
1041 .name("foo")
1042 .target_name("foo_from_a_svc")
1043 .source(OfferSource::Self_)
1044 .target_static_child("b"),
1045 )
1046 .offer(
1047 OfferBuilder::directory()
1048 .name("foo_from_d_data")
1049 .source_static_child("b")
1050 .target_static_child("c")
1051 .rights(fio::R_STAR_DIR),
1052 )
1053 .child_default("b")
1054 .child_default("c")
1055 .build(),
1056 ),
1057 (
1058 "b",
1059 ComponentDeclBuilder::new_empty_component()
1060 .offer(
1061 OfferBuilder::directory()
1062 .name("foo_from_d_data")
1063 .source_static_child("d")
1064 .target_static_child("e")
1065 .rights(fio::R_STAR_DIR),
1066 )
1067 .offer(
1068 OfferBuilder::protocol()
1069 .name("foo_from_a_svc")
1070 .source(OfferSource::Parent)
1071 .target_static_child("e"),
1072 )
1073 .expose(
1074 ExposeBuilder::directory()
1075 .name("foo_from_d_data")
1076 .source_static_child("d")
1077 .rights(fio::R_STAR_DIR),
1078 )
1079 .child_default("d")
1080 .child_default("e")
1081 .build(),
1082 ),
1083 (
1084 "c",
1085 ComponentDeclBuilder::new_empty_component()
1086 .offer(
1087 OfferBuilder::directory()
1088 .name("foo_from_d_data")
1089 .source(OfferSource::Parent)
1090 .target_static_child("f")
1091 .rights(fio::R_STAR_DIR),
1092 )
1093 .offer(
1094 OfferBuilder::protocol()
1095 .name("foo_from_h_svc")
1096 .source_static_child("g")
1097 .target_static_child("f"),
1098 )
1099 .child_default("f")
1100 .child_default("g")
1101 .build(),
1102 ),
1103 (
1104 "d",
1105 ComponentDeclBuilder::new()
1106 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
1107 .expose(
1108 ExposeBuilder::directory()
1109 .name("foo_data")
1110 .source(ExposeSource::Self_)
1111 .target_name("foo_from_d_data")
1112 .rights(fio::R_STAR_DIR),
1113 )
1114 .build(),
1115 ),
1116 (
1117 "e",
1118 ComponentDeclBuilder::new()
1119 .use_(UseBuilder::directory().name("foo_from_d_data").path("/data/hippo"))
1120 .use_(UseBuilder::protocol().name("foo_from_a_svc").path("/svc/hippo"))
1121 .build(),
1122 ),
1123 (
1124 "f",
1125 ComponentDeclBuilder::new()
1126 .use_(UseBuilder::directory().name("foo_from_d_data").path("/data/hippo"))
1127 .use_(UseBuilder::protocol().name("foo_from_h_svc").path("/svc/hippo"))
1128 .build(),
1129 ),
1130 (
1131 "g",
1132 ComponentDeclBuilder::new_empty_component()
1133 .expose(
1134 ExposeBuilder::protocol().name("foo_from_h_svc").source_static_child("h"),
1135 )
1136 .child_default("h")
1137 .build(),
1138 ),
1139 (
1140 "h",
1141 ComponentDeclBuilder::new()
1142 .protocol_default("foo")
1143 .expose(
1144 ExposeBuilder::protocol()
1145 .name("foo")
1146 .target_name("foo_from_h_svc")
1147 .source(ExposeSource::Self_),
1148 )
1149 .build(),
1150 ),
1151 ];
1152 let model = T::new("a", components).build().await;
1153 model
1154 .check_use(
1155 vec!["b", "e"].try_into().unwrap(),
1156 CheckUse::default_directory(ExpectedResult::Ok),
1157 )
1158 .await;
1159 model
1160 .check_use(
1161 vec!["b", "e"].try_into().unwrap(),
1162 CheckUse::Protocol {
1163 path: default_service_capability(),
1164 expected_res: ExpectedResult::Ok,
1165 },
1166 )
1167 .await;
1168 model
1169 .check_use(
1170 vec!["c", "f"].try_into().unwrap(),
1171 CheckUse::default_directory(ExpectedResult::Ok),
1172 )
1173 .await;
1174 model
1175 .check_use(
1176 vec!["c", "f"].try_into().unwrap(),
1177 CheckUse::Protocol {
1178 path: default_service_capability(),
1179 expected_res: ExpectedResult::Ok,
1180 },
1181 )
1182 .await;
1183 }
1184
1185 pub async fn test_use_from_component_manager_namespace(&self) {
1192 let components = vec![(
1193 "a",
1194 ComponentDeclBuilder::new()
1195 .use_(UseBuilder::directory().name("foo_data").path("/data/hippo"))
1196 .use_(UseBuilder::protocol().name("foo").path("/svc/hippo"))
1197 .build(),
1198 )];
1199 let namespace_capabilities = vec![
1200 CapabilityBuilder::directory()
1201 .name("foo_data")
1202 .path("/use_from_cm_namespace/data/foo")
1203 .build(),
1204 CapabilityBuilder::protocol()
1205 .name("foo")
1206 .path("/use_from_cm_namespace/svc/foo")
1207 .build(),
1208 ];
1209 let mut builder = T::new("a", components);
1210 builder.set_namespace_capabilities(namespace_capabilities);
1211 let model = builder.build().await;
1212
1213 model.install_namespace_directory("/use_from_cm_namespace");
1214 model.check_use(Moniker::root(), CheckUse::default_directory(ExpectedResult::Ok)).await;
1215 model
1216 .check_use(
1217 Moniker::root(),
1218 CheckUse::Protocol {
1219 path: default_service_capability(),
1220 expected_res: ExpectedResult::Ok,
1221 },
1222 )
1223 .await;
1224 }
1225
1226 pub async fn test_offer_from_component_manager_namespace(&self) {
1237 let components = vec![
1238 (
1239 "a",
1240 ComponentDeclBuilder::new()
1241 .offer(
1242 OfferBuilder::directory()
1243 .name("foo_data")
1244 .target_name("bar_data")
1245 .source(OfferSource::Parent)
1246 .target_static_child("b")
1247 .rights(fio::R_STAR_DIR),
1248 )
1249 .offer(
1250 OfferBuilder::protocol()
1251 .name("foo")
1252 .target_name("bar")
1253 .source(OfferSource::Parent)
1254 .target_static_child("b"),
1255 )
1256 .child_default("b")
1257 .build(),
1258 ),
1259 (
1260 "b",
1261 ComponentDeclBuilder::new()
1262 .use_(UseBuilder::directory().name("bar_data").path("/data/hippo"))
1263 .use_(UseBuilder::protocol().name("bar").path("/svc/hippo"))
1264 .build(),
1265 ),
1266 ];
1267 let namespace_capabilities = vec![
1268 CapabilityBuilder::directory()
1269 .name("foo_data")
1270 .path("/offer_from_cm_namespace/data/foo")
1271 .build(),
1272 CapabilityBuilder::protocol()
1273 .name("foo")
1274 .path("/offer_from_cm_namespace/svc/foo")
1275 .build(),
1276 ];
1277 let mut builder = T::new("a", components);
1278 builder.set_namespace_capabilities(namespace_capabilities);
1279 let model = builder.build().await;
1280
1281 model.install_namespace_directory("/offer_from_cm_namespace");
1282 model
1283 .check_use(
1284 vec!["b"].try_into().unwrap(),
1285 CheckUse::default_directory(ExpectedResult::Ok),
1286 )
1287 .await;
1288 model
1289 .check_use(
1290 vec!["b"].try_into().unwrap(),
1291 CheckUse::Protocol {
1292 path: default_service_capability(),
1293 expected_res: ExpectedResult::Ok,
1294 },
1295 )
1296 .await;
1297 }
1298
1299 pub async fn test_use_not_offered(&self) {
1306 let components = vec![
1307 ("a", ComponentDeclBuilder::new_empty_component().child_default("b").build()),
1308 (
1309 "b",
1310 ComponentDeclBuilder::new()
1311 .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1312 .use_(UseBuilder::protocol().name("hippo"))
1313 .build(),
1314 ),
1315 ];
1316 let model = T::new("a", components).build().await;
1317 model
1318 .check_use(
1319 vec!["b"].try_into().unwrap(),
1320 CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1321 )
1322 .await;
1323 model
1324 .check_use(
1325 vec!["b"].try_into().unwrap(),
1326 CheckUse::Protocol {
1327 path: default_service_capability(),
1328 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1329 },
1330 )
1331 .await;
1332 }
1333
1334 pub async fn test_use_offer_source_not_exposed(&self) {
1343 let components = vec![
1344 (
1345 "a",
1346 ComponentDeclBuilder::new_empty_component()
1347 .offer(
1348 OfferBuilder::directory()
1349 .name("hippo_data")
1350 .source_static_child("b")
1351 .target_static_child("c")
1352 .rights(fio::R_STAR_DIR),
1353 )
1354 .offer(
1355 OfferBuilder::protocol()
1356 .name("hippo")
1357 .source_static_child("b")
1358 .target_static_child("c"),
1359 )
1360 .child_default("b")
1361 .child_default("c")
1362 .build(),
1363 ),
1364 ("b", component_decl_with_test_runner()),
1365 (
1366 "c",
1367 ComponentDeclBuilder::new()
1368 .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1369 .use_(UseBuilder::protocol().name("hippo"))
1370 .build(),
1371 ),
1372 ];
1373 let model = T::new("a", components).build().await;
1374 model
1375 .check_use(
1376 vec!["c"].try_into().unwrap(),
1377 CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1378 )
1379 .await;
1380 model
1381 .check_use(
1382 vec!["c"].try_into().unwrap(),
1383 CheckUse::Protocol {
1384 path: default_service_capability(),
1385 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1386 },
1387 )
1388 .await;
1389 }
1390
1391 pub async fn test_use_offer_source_not_offered(&self) {
1402 let components = vec![
1403 ("a", ComponentDeclBuilder::new().child_default("b").build()),
1404 (
1405 "b",
1406 ComponentDeclBuilder::new_empty_component()
1407 .offer(
1408 OfferBuilder::directory()
1409 .name("hippo_data")
1410 .source(OfferSource::Parent)
1411 .target_static_child("c")
1412 .rights(fio::R_STAR_DIR),
1413 )
1414 .offer(
1415 OfferBuilder::protocol()
1416 .name("hippo")
1417 .source(OfferSource::Parent)
1418 .target_static_child("c"),
1419 )
1420 .child_default("c")
1421 .build(),
1422 ),
1423 (
1424 "c",
1425 ComponentDeclBuilder::new()
1426 .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1427 .use_(UseBuilder::protocol().name("hippo"))
1428 .build(),
1429 ),
1430 ];
1431 let test = T::new("a", components).build().await;
1432 test.check_use(
1433 vec!["b", "c"].try_into().unwrap(),
1434 CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1435 )
1436 .await;
1437 test.check_use(
1438 vec!["b", "c"].try_into().unwrap(),
1439 CheckUse::Protocol {
1440 path: default_service_capability(),
1441 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1442 },
1443 )
1444 .await;
1445 }
1446
1447 pub async fn test_use_from_expose(&self) {
1458 let components = vec![
1459 ("a", ComponentDeclBuilder::new_empty_component().child_default("b").build()),
1460 (
1461 "b",
1462 ComponentDeclBuilder::new()
1463 .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1464 .use_(UseBuilder::protocol().name("hippo"))
1465 .child_default("c")
1466 .build(),
1467 ),
1468 (
1469 "c",
1470 ComponentDeclBuilder::new()
1471 .capability(CapabilityBuilder::directory().name("hippo_data").path("/data/foo"))
1472 .protocol_default("hippo")
1473 .expose(
1474 ExposeBuilder::directory()
1475 .name("hippo_data")
1476 .source(ExposeSource::Self_)
1477 .rights(fio::R_STAR_DIR),
1478 )
1479 .expose(ExposeBuilder::protocol().name("hippo").source(ExposeSource::Self_))
1480 .build(),
1481 ),
1482 ];
1483 let model = T::new("a", components).build().await;
1484 model
1485 .check_use(
1486 vec!["b"].try_into().unwrap(),
1487 CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1488 )
1489 .await;
1490 model
1491 .check_use(
1492 vec!["b"].try_into().unwrap(),
1493 CheckUse::Protocol {
1494 path: default_service_capability(),
1495 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1496 },
1497 )
1498 .await;
1499 }
1500
1501 pub async fn test_route_protocol_from_expose(&self) {
1508 let expose_decl = ExposeBuilder::protocol()
1509 .name("foo")
1510 .source(ExposeSource::Child("b".parse().unwrap()))
1511 .build();
1512 let expected_protocol_decl = CapabilityBuilder::protocol().name("foo").build();
1513
1514 let components = vec![
1515 (
1516 "a",
1517 ComponentDeclBuilder::new().expose(expose_decl.clone()).child_default("b").build(),
1518 ),
1519 (
1520 "b",
1521 ComponentDeclBuilder::new()
1522 .expose(ExposeBuilder::protocol().name("foo").source(ExposeSource::Self_))
1523 .capability(expected_protocol_decl.clone())
1524 .build(),
1525 ),
1526 ];
1527 let model = T::new("a", components).build().await;
1528 let root_instance = model.look_up_instance(&Moniker::root()).await.expect("root instance");
1529 let expected_source_moniker = Moniker::parse_str("/b").unwrap();
1530
1531 let CapabilityDecl::Protocol(expected_protocol_decl) = expected_protocol_decl else {
1532 unreachable!();
1533 };
1534 let ExposeDecl::Protocol(expose_decl) = expose_decl else {
1535 unreachable!();
1536 };
1537 assert_matches!(
1538 route_capability(RouteRequest::ExposeProtocol(expose_decl), &root_instance, &mut NoopRouteMapper).await,
1539 Ok(RouteSource {
1540 source: CapabilitySource::Component(ComponentSource {
1541 capability: ComponentCapability::Protocol(capability_decl),
1542 moniker,
1543 }),
1544 relative_path,
1545 }) if capability_decl == expected_protocol_decl && moniker == expected_source_moniker && relative_path.is_dot()
1546 );
1547 }
1548
1549 pub async fn test_use_from_expose_to_framework(&self) {
1557 let components = vec![
1558 (
1559 "a",
1560 ComponentDeclBuilder::new()
1561 .offer(
1562 OfferBuilder::directory()
1563 .name("bar_data")
1564 .target_name("baz_data")
1565 .source_static_child("b")
1566 .target_static_child("c")
1567 .rights(fio::R_STAR_DIR),
1568 )
1569 .offer(
1570 OfferBuilder::protocol()
1571 .name("bar")
1572 .target_name("baz")
1573 .source_static_child("b")
1574 .target_static_child("c"),
1575 )
1576 .child_default("b")
1577 .child_default("c")
1578 .build(),
1579 ),
1580 (
1581 "b",
1582 ComponentDeclBuilder::new()
1583 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
1584 .protocol_default("foo")
1585 .expose(
1586 ExposeBuilder::directory()
1587 .name("foo_data")
1588 .source(ExposeSource::Self_)
1589 .target_name("bar_data")
1590 .target(ExposeTarget::Framework)
1591 .rights(fio::R_STAR_DIR),
1592 )
1593 .expose(
1594 ExposeBuilder::protocol()
1595 .name("foo")
1596 .target_name("bar")
1597 .source(ExposeSource::Self_)
1598 .target(ExposeTarget::Framework),
1599 )
1600 .build(),
1601 ),
1602 (
1603 "c",
1604 ComponentDeclBuilder::new()
1605 .use_(UseBuilder::directory().name("baz_data").path("/data/hippo"))
1606 .use_(UseBuilder::protocol().name("baz").path("/svc/hippo"))
1607 .build(),
1608 ),
1609 ];
1610 let model = T::new("a", components).build().await;
1611 model
1612 .check_use(
1613 vec!["c"].try_into().unwrap(),
1614 CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1615 )
1616 .await;
1617 model
1618 .check_use(
1619 vec!["c"].try_into().unwrap(),
1620 CheckUse::Protocol {
1621 path: default_service_capability(),
1622 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1623 },
1624 )
1625 .await;
1626 }
1627
1628 pub async fn test_offer_from_non_executable(&self) {
1637 let components = vec![
1638 (
1639 "a",
1640 ComponentDeclBuilder::new_empty_component()
1641 .capability(CapabilityBuilder::directory().name("hippo_data").path("/data"))
1642 .protocol_default("hippo")
1643 .offer(
1644 OfferBuilder::directory()
1645 .name("hippo_data")
1646 .source(OfferSource::Self_)
1647 .target_static_child("b")
1648 .rights(fio::R_STAR_DIR),
1649 )
1650 .offer(
1651 OfferBuilder::protocol()
1652 .name("hippo")
1653 .source(OfferSource::Self_)
1654 .target_static_child("b"),
1655 )
1656 .child_default("b")
1657 .build(),
1658 ),
1659 (
1660 "b",
1661 ComponentDeclBuilder::new()
1662 .use_(UseBuilder::directory().name("hippo_data").path("/data/hippo"))
1663 .use_(UseBuilder::protocol().name("hippo"))
1664 .build(),
1665 ),
1666 ];
1667 let model = T::new("a", components).build().await;
1668 model
1669 .check_use(
1670 vec!["b"].try_into().unwrap(),
1671 CheckUse::default_directory(ExpectedResult::Err(zx::Status::NOT_FOUND)),
1672 )
1673 .await;
1674 model
1675 .check_use(
1676 vec!["b"].try_into().unwrap(),
1677 CheckUse::Protocol {
1678 path: default_service_capability(),
1679 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
1680 },
1681 )
1682 .await;
1683 }
1684
1685 pub async fn test_route_filtered_aggregate_service(&self) {
1695 let expected_service_decl = CapabilityBuilder::service().name("foo").build();
1696 let components = vec![
1697 (
1698 "a",
1699 ComponentDeclBuilder::new()
1700 .offer(
1701 OfferBuilder::service()
1702 .name("foo")
1703 .source_static_child("b")
1704 .target_static_child("d")
1705 .source_instance_filter(["instance_0", "instance_1"]),
1706 )
1707 .offer(
1708 OfferBuilder::service()
1709 .name("foo")
1710 .source_static_child("c")
1711 .target_static_child("d")
1712 .source_instance_filter(["instance_2", "instance_3"]),
1713 )
1714 .child_default("b")
1715 .child_default("c")
1716 .child_default("d")
1717 .build(),
1718 ),
1719 (
1720 "b",
1721 ComponentDeclBuilder::new()
1722 .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
1723 .capability(expected_service_decl.clone())
1724 .build(),
1725 ),
1726 (
1727 "c",
1728 ComponentDeclBuilder::new()
1729 .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
1730 .capability(expected_service_decl.clone())
1731 .build(),
1732 ),
1733 ("d", ComponentDeclBuilder::new().use_(UseBuilder::service().name("foo")).build()),
1734 ];
1735 let model = T::new("a", components).build().await;
1736
1737 let d_component =
1738 model.look_up_instance(&vec!["d"].try_into().unwrap()).await.expect("b instance");
1739
1740 let source = route_capability(
1741 RouteRequest::UseService(UseServiceDecl {
1742 source: UseSource::Parent,
1743 source_name: "foo".parse().unwrap(),
1744 source_dictionary: Default::default(),
1745 target_path: "/svc/foo".parse().unwrap(),
1746 dependency_type: DependencyType::Strong,
1747 availability: Availability::Required,
1748 }),
1749 &d_component,
1750 &mut NoopRouteMapper,
1751 )
1752 .await
1753 .expect("failed to route service");
1754 match source {
1755 RouteSource {
1756 source:
1757 CapabilitySource::FilteredAggregateProvider(FilteredAggregateProviderSource {
1758 capability: AggregateCapability::Service(name),
1759 ..
1760 }),
1761 relative_path,
1762 } if relative_path.is_dot() => {
1763 assert_eq!(name, "foo");
1764 }
1765 _ => panic!("bad capability source"),
1766 };
1767 }
1768
1769 pub async fn test_route_anonymized_aggregate_service(&self) {
1781 let expected_service_decl = CapabilityBuilder::service().name("foo").build();
1782 let components = vec![
1783 (
1784 "a",
1785 ComponentDeclBuilder::new()
1786 .offer(
1787 OfferBuilder::service()
1788 .name("foo")
1789 .source(OfferSource::Self_)
1790 .target_static_child("b"),
1791 )
1792 .capability(expected_service_decl.clone())
1793 .child_default("b")
1794 .build(),
1795 ),
1796 (
1797 "b",
1798 ComponentDeclBuilder::new()
1799 .offer(
1800 OfferBuilder::service()
1801 .name("foo")
1802 .source_static_child("c")
1803 .target_static_child("d"),
1804 )
1805 .offer(
1806 OfferBuilder::service()
1807 .name("foo")
1808 .source(OfferSource::Parent)
1809 .target_static_child("d"),
1810 )
1811 .offer(
1812 OfferBuilder::service()
1813 .name("foo")
1814 .source(OfferSource::Self_)
1815 .target_static_child("d"),
1816 )
1817 .capability(expected_service_decl.clone())
1818 .child_default("c")
1819 .child_default("d")
1820 .build(),
1821 ),
1822 (
1823 "c",
1824 ComponentDeclBuilder::new()
1825 .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
1826 .capability(expected_service_decl.clone())
1827 .build(),
1828 ),
1829 ("d", ComponentDeclBuilder::new().use_(UseBuilder::service().name("foo")).build()),
1830 ];
1831 let test = T::new("a", components).build().await;
1832
1833 let d_component = test.look_up_instance(&"b/d".parse().unwrap()).await.expect("b instance");
1834 let source = route_capability(
1835 RouteRequest::UseService(UseServiceDecl {
1836 source: UseSource::Parent,
1837 source_name: "foo".parse().unwrap(),
1838 source_dictionary: Default::default(),
1839 target_path: "/svc/foo".parse().unwrap(),
1840 dependency_type: DependencyType::Strong,
1841 availability: Availability::Required,
1842 }),
1843 &d_component,
1844 &mut NoopRouteMapper,
1845 )
1846 .await
1847 .unwrap();
1848 match source {
1849 RouteSource {
1850 source:
1851 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
1852 capability: AggregateCapability::Service(name),
1853 members,
1854 ..
1855 }),
1856 relative_path,
1857 } if relative_path.is_dot() => {
1858 assert_eq!(name, "foo");
1859 assert_eq!(members.len(), 3);
1860 for c in [
1861 AggregateMember::Child(ChildRef {
1862 name: "c".parse().unwrap(),
1863 collection: None,
1864 }),
1865 AggregateMember::Parent,
1866 AggregateMember::Self_,
1867 ] {
1868 assert!(members.contains(&c));
1869 }
1870 }
1871 _ => panic!("bad capability source"),
1872 }
1873 }
1874
1875 pub async fn test_use_directory_with_subdir_from_grandparent(&self) {
1885 let components = vec![
1886 (
1887 "a",
1888 ComponentDeclBuilder::new()
1889 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
1890 .protocol_default("foo")
1891 .offer(
1892 OfferBuilder::directory()
1893 .name("foo_data")
1894 .source(OfferSource::Self_)
1895 .target_static_child("b")
1896 .rights(fio::R_STAR_DIR)
1897 .subdir("s1/s2"),
1898 )
1899 .child_default("b")
1900 .build(),
1901 ),
1902 (
1903 "b",
1904 ComponentDeclBuilder::new()
1905 .offer(
1906 OfferBuilder::directory()
1907 .name("foo_data")
1908 .source(OfferSource::Parent)
1909 .target_static_child("c")
1910 .rights(fio::R_STAR_DIR)
1911 .subdir("s3"),
1912 )
1913 .child_default("c")
1914 .build(),
1915 ),
1916 (
1917 "c",
1918 ComponentDeclBuilder::new()
1919 .use_(UseBuilder::directory().name("foo_data").path("/data/hippo").subdir("s4"))
1920 .build(),
1921 ),
1922 ];
1923 let model = T::new("a", components).build().await;
1924 model
1925 .create_static_file(Path::new("foo/s1/s2/s3/s4/inner"), "hello")
1926 .await
1927 .expect("failed to create file");
1928 model
1929 .check_use(
1930 vec!["b", "c"].try_into().unwrap(),
1931 CheckUse::Directory {
1932 path: default_directory_capability(),
1933 file: PathBuf::from("inner"),
1934 expected_res: ExpectedResult::Ok,
1935 },
1936 )
1937 .await;
1938 }
1939
1940 pub async fn test_use_directory_with_subdir_from_sibling(&self) {
1949 let components = vec![
1950 (
1951 "a",
1952 ComponentDeclBuilder::new()
1953 .offer(
1954 OfferBuilder::directory()
1955 .name("foo_data")
1956 .source_static_child("b")
1957 .target_static_child("c")
1958 .rights(fio::R_STAR_DIR)
1959 .subdir("s3"),
1960 )
1961 .child_default("b")
1962 .child_default("c")
1963 .build(),
1964 ),
1965 (
1966 "b",
1967 ComponentDeclBuilder::new()
1968 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
1969 .expose(
1970 ExposeBuilder::directory()
1971 .name("foo_data")
1972 .source(ExposeSource::Self_)
1973 .rights(fio::R_STAR_DIR)
1974 .subdir("s1/s2"),
1975 )
1976 .build(),
1977 ),
1978 (
1979 "c",
1980 ComponentDeclBuilder::new()
1981 .use_(UseBuilder::directory().name("foo_data").path("/data/hippo"))
1982 .build(),
1983 ),
1984 ];
1985 let model = T::new("a", components).build().await;
1986 model
1987 .create_static_file(Path::new("foo/s1/s2/s3/inner"), "hello")
1988 .await
1989 .expect("failed to create file");
1990 model
1991 .check_use(
1992 vec!["c"].try_into().unwrap(),
1993 CheckUse::Directory {
1994 path: default_directory_capability(),
1995 file: PathBuf::from("inner"),
1996 expected_res: ExpectedResult::Ok,
1997 },
1998 )
1999 .await;
2000 }
2001
2002 pub async fn test_expose_directory_with_subdir(&self) {
2013 let components = vec![
2014 (
2015 "a",
2016 ComponentDeclBuilder::new()
2017 .expose(
2018 ExposeBuilder::directory()
2019 .name("foo_data")
2020 .source_static_child("b")
2021 .target_name("hippo_data")
2022 .subdir("s3"),
2023 )
2024 .child_default("b")
2025 .build(),
2026 ),
2027 (
2028 "b",
2029 ComponentDeclBuilder::new()
2030 .expose(
2031 ExposeBuilder::directory()
2032 .name("foo_data")
2033 .source_static_child("c")
2034 .subdir("s1/s2"),
2035 )
2036 .child_default("c")
2037 .build(),
2038 ),
2039 (
2040 "c",
2041 ComponentDeclBuilder::new()
2042 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2043 .expose(
2044 ExposeBuilder::directory()
2045 .name("foo_data")
2046 .source(ExposeSource::Self_)
2047 .rights(fio::R_STAR_DIR),
2048 )
2049 .build(),
2050 ),
2051 ];
2052 let model = T::new("a", components).build().await;
2053 model
2054 .create_static_file(Path::new("foo/s1/s2/s3/inner"), "hello")
2055 .await
2056 .expect("failed to create file");
2057 model
2058 .check_use_exposed_dir(
2059 Moniker::root(),
2060 CheckUse::Directory {
2061 path: "/hippo_data".parse().unwrap(),
2062 file: PathBuf::from("inner"),
2063 expected_res: ExpectedResult::Ok,
2064 },
2065 )
2066 .await;
2067 }
2068
2069 pub async fn test_expose_from_self_and_child(&self) {
2070 let components = vec![
2071 ("a", ComponentDeclBuilder::new().child_default("b").build()),
2072 (
2073 "b",
2074 ComponentDeclBuilder::new()
2075 .expose(
2076 ExposeBuilder::directory()
2077 .name("hippo_data")
2078 .source_static_child("c")
2079 .target_name("hippo_bar_data")
2080 .rights(fio::R_STAR_DIR),
2081 )
2082 .expose(
2083 ExposeBuilder::protocol()
2084 .name("hippo")
2085 .target_name("hippo_bar")
2086 .source_static_child("c"),
2087 )
2088 .child_default("c")
2089 .build(),
2090 ),
2091 (
2092 "c",
2093 ComponentDeclBuilder::new()
2094 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2095 .protocol_default("foo")
2096 .expose(
2097 ExposeBuilder::directory()
2098 .name("foo_data")
2099 .source(ExposeSource::Self_)
2100 .target_name("hippo_data")
2101 .rights(fio::R_STAR_DIR),
2102 )
2103 .expose(
2104 ExposeBuilder::protocol()
2105 .name("foo")
2106 .target_name("hippo")
2107 .source(ExposeSource::Self_),
2108 )
2109 .build(),
2110 ),
2111 ];
2112 let model = T::new("a", components).build().await;
2113 model
2114 .check_use_exposed_dir(
2115 vec!["b"].try_into().unwrap(),
2116 CheckUse::Directory {
2117 path: "/hippo_bar_data".parse().unwrap(),
2118 file: PathBuf::from("hippo"),
2119 expected_res: ExpectedResult::Ok,
2120 },
2121 )
2122 .await;
2123 model
2124 .check_use_exposed_dir(
2125 vec!["b"].try_into().unwrap(),
2126 CheckUse::Protocol {
2127 path: "/hippo_bar".parse().unwrap(),
2128 expected_res: ExpectedResult::Ok,
2129 },
2130 )
2131 .await;
2132 model
2133 .check_use_exposed_dir(
2134 vec!["b", "c"].try_into().unwrap(),
2135 CheckUse::Directory {
2136 path: "/hippo_data".parse().unwrap(),
2137 file: PathBuf::from("hippo"),
2138 expected_res: ExpectedResult::Ok,
2139 },
2140 )
2141 .await;
2142 model
2143 .check_use_exposed_dir(
2144 vec!["b", "c"].try_into().unwrap(),
2145 CheckUse::Protocol {
2146 path: "/hippo".parse().unwrap(),
2147 expected_res: ExpectedResult::Ok,
2148 },
2149 )
2150 .await;
2151 }
2152
2153 pub async fn test_use_not_exposed(&self) {
2154 let components = vec![
2155 ("a", ComponentDeclBuilder::new().child_default("b").build()),
2156 ("b", ComponentDeclBuilder::new().child_default("c").build()),
2157 (
2158 "c",
2159 ComponentDeclBuilder::new()
2160 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2161 .protocol_default("foo")
2162 .expose(
2163 ExposeBuilder::directory()
2164 .name("foo_data")
2165 .source(ExposeSource::Self_)
2166 .target_name("hippo_data")
2167 .rights(fio::R_STAR_DIR),
2168 )
2169 .expose(
2170 ExposeBuilder::protocol()
2171 .name("foo")
2172 .target_name("hippo")
2173 .source(ExposeSource::Self_),
2174 )
2175 .build(),
2176 ),
2177 ];
2178 let model = T::new("a", components).build().await;
2179 model
2184 .check_use_exposed_dir(
2185 vec!["b"].try_into().unwrap(),
2186 CheckUse::Directory {
2187 path: "/hippo_data".parse().unwrap(),
2188 file: PathBuf::from("hippo"),
2189 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2190 },
2191 )
2192 .await;
2193 model
2194 .check_use_exposed_dir(
2195 vec!["b"].try_into().unwrap(),
2196 CheckUse::Protocol {
2197 path: "/hippo".parse().unwrap(),
2198 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2199 },
2200 )
2201 .await;
2202 model
2203 .check_use_exposed_dir(
2204 vec!["b", "c"].try_into().unwrap(),
2205 CheckUse::Directory {
2206 path: "/hippo_data".parse().unwrap(),
2207 file: PathBuf::from("hippo"),
2208 expected_res: ExpectedResult::Ok,
2209 },
2210 )
2211 .await;
2212 model
2213 .check_use_exposed_dir(
2214 vec!["b", "c"].try_into().unwrap(),
2215 CheckUse::Protocol {
2216 path: "/hippo".parse().unwrap(),
2217 expected_res: ExpectedResult::Ok,
2218 },
2219 )
2220 .await;
2221 }
2222
2223 pub async fn test_expose_to_framework_from_self(&self) {
2224 let components = vec![(
2225 "a",
2226 ComponentDeclBuilder::new()
2227 .protocol_default("foo")
2228 .expose(
2229 ExposeBuilder::protocol()
2230 .name("foo")
2231 .target_name("hippo")
2232 .target(ExposeTarget::Framework)
2233 .source(ExposeSource::Self_),
2234 )
2235 .build(),
2236 )];
2237 let model = T::new("a", components).build().await;
2238 model
2239 .check_exposed_to_framework(
2240 Moniker::root(),
2241 CheckUse::Protocol {
2242 path: "/hippo".parse().unwrap(),
2243 expected_res: ExpectedResult::Ok,
2244 },
2245 )
2246 .await;
2247 }
2248
2249 pub async fn test_expose_to_framework_from_child(&self) {
2250 let components = vec![
2251 (
2252 "a",
2253 ComponentDeclBuilder::new()
2254 .child_default("b")
2255 .expose(
2256 ExposeBuilder::protocol()
2257 .name("foo")
2258 .target_name("hippo")
2259 .target(ExposeTarget::Framework)
2260 .source(ExposeSource::Child("b".parse().unwrap())),
2261 )
2262 .build(),
2263 ),
2264 (
2265 "b",
2266 ComponentDeclBuilder::new()
2267 .protocol_default("foo")
2268 .expose(ExposeBuilder::protocol().name("foo").source(ExposeSource::Self_))
2269 .build(),
2270 ),
2271 ];
2272 let model = T::new("a", components).build().await;
2273 model
2274 .check_exposed_to_framework(
2275 Moniker::root(),
2276 CheckUse::Protocol {
2277 path: "/hippo".parse().unwrap(),
2278 expected_res: ExpectedResult::Ok,
2279 },
2280 )
2281 .await;
2282 }
2283
2284 pub async fn test_expose_to_parent_and_framework(&self) {
2285 let components = vec![
2286 ("a", ComponentDeclBuilder::new().child_default("b").build()),
2287 (
2288 "b",
2289 ComponentDeclBuilder::new()
2290 .protocol_default("foo")
2291 .expose(
2292 ExposeBuilder::protocol()
2293 .name("foo")
2294 .target_name("hippo")
2295 .source(ExposeSource::Self_),
2296 )
2297 .expose(
2298 ExposeBuilder::protocol()
2299 .name("foo")
2300 .target(ExposeTarget::Framework)
2301 .source(ExposeSource::Self_),
2302 )
2303 .build(),
2304 ),
2305 ];
2306 let model = T::new("a", components).build().await;
2307 model
2308 .check_exposed_to_framework(
2309 vec!["b"].try_into().unwrap(),
2310 CheckUse::Protocol {
2311 path: "/hippo".parse().unwrap(),
2312 expected_res: ExpectedResult::Ok,
2313 },
2314 )
2315 .await;
2316 model
2317 .check_use_exposed_dir(
2318 vec!["b"].try_into().unwrap(),
2319 CheckUse::Protocol {
2320 path: "/hippo".parse().unwrap(),
2321 expected_res: ExpectedResult::Ok,
2322 },
2323 )
2324 .await;
2325 }
2326
2327 pub async fn test_invalid_use_from_component_manager(&self) {
2333 let components = vec![(
2334 "a",
2335 ComponentDeclBuilder::new()
2336 .use_(UseBuilder::protocol().name("invalid").path("/svc/valid"))
2337 .build(),
2338 )];
2339
2340 let model = T::new("a", components).build().await;
2342 model
2343 .check_use(
2344 Moniker::root(),
2345 CheckUse::Protocol {
2346 path: "/svc/valid".parse().unwrap(),
2347 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2348 },
2349 )
2350 .await;
2351 }
2352
2353 pub async fn test_invalid_offer_from_component_manager(&self) {
2362 let components = vec![
2363 (
2364 "a",
2365 ComponentDeclBuilder::new()
2366 .offer(
2367 OfferBuilder::protocol()
2368 .name("invalid")
2369 .target_name("valid")
2370 .source(OfferSource::Parent)
2371 .target_static_child("b"),
2372 )
2373 .child_default("b")
2374 .build(),
2375 ),
2376 ("b", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("valid")).build()),
2377 ];
2378
2379 let model = T::new("a", components).build().await;
2381 model
2382 .check_use(
2383 vec!["b"].try_into().unwrap(),
2384 CheckUse::Protocol {
2385 path: "/svc/valid".parse().unwrap(),
2386 expected_res: ExpectedResult::Err(zx::Status::NOT_FOUND),
2387 },
2388 )
2389 .await;
2390 }
2391
2392 pub async fn test_event_stream_aliasing(&self) {
2495 let components = vec![
2496 ("root", ComponentDeclBuilder::new().child_default("a").build()),
2497 (
2498 "a",
2499 ComponentDeclBuilder::new()
2500 .offer(
2501 OfferBuilder::event_stream()
2502 .name("started")
2503 .source(OfferSource::Parent)
2504 .target(OfferTarget::Child(ChildRef {
2505 name: "b".parse().unwrap(),
2506 collection: None,
2507 }))
2508 .scope(vec![EventScope::Child(ChildRef {
2509 name: "b".parse().unwrap(),
2510 collection: None,
2511 })]),
2512 )
2513 .offer(
2514 OfferBuilder::event_stream()
2515 .name("started")
2516 .source(OfferSource::Parent)
2517 .target(OfferTarget::Child(ChildRef {
2518 name: "d".parse().unwrap(),
2519 collection: None,
2520 }))
2521 .scope(vec![EventScope::Child(ChildRef {
2522 name: "c".parse().unwrap(),
2523 collection: None,
2524 })]),
2525 )
2526 .offer(
2527 OfferBuilder::event_stream()
2528 .name("started")
2529 .source(OfferSource::Parent)
2530 .target(OfferTarget::Child(ChildRef {
2531 name: "c".parse().unwrap(),
2532 collection: None,
2533 }))
2534 .scope(vec![EventScope::Child(ChildRef {
2535 name: "d".parse().unwrap(),
2536 collection: None,
2537 })]),
2538 )
2539 .child_default("b")
2540 .child_default("c")
2541 .child_default("d")
2542 .build(),
2543 ),
2544 (
2545 "b",
2546 ComponentDeclBuilder::new()
2547 .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2548 .build(),
2549 ),
2550 (
2551 "c",
2552 ComponentDeclBuilder::new()
2553 .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2554 .build(),
2555 ),
2556 (
2557 "d",
2558 ComponentDeclBuilder::new()
2559 .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2560 .build(),
2561 ),
2562 ];
2563
2564 let mut builder = T::new("a", components);
2565 builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2566 name: "started".parse().unwrap(),
2567 })]);
2568
2569 let model = builder.build().await;
2570 model
2571 .check_use(
2572 vec!["b"].try_into().unwrap(),
2573 CheckUse::EventStream {
2574 expected_res: ExpectedResult::Ok,
2575 path: "/event/stream".parse().unwrap(),
2576 scope: vec![ComponentEventRoute {
2577 component: "/".to_string(),
2578 scope: Some(vec!["b".to_string()]),
2579 }],
2580 name: "started".parse().unwrap(),
2581 },
2582 )
2583 .await;
2584 model
2585 .check_use(
2586 vec!["c"].try_into().unwrap(),
2587 CheckUse::EventStream {
2588 expected_res: ExpectedResult::Ok,
2589 path: "/event/stream".parse().unwrap(),
2590 scope: vec![ComponentEventRoute {
2591 component: "/".to_string(),
2592 scope: Some(vec!["d".to_string()]),
2593 }],
2594 name: "started".parse().unwrap(),
2595 },
2596 )
2597 .await;
2598
2599 model
2600 .check_use(
2601 vec!["d"].try_into().unwrap(),
2602 CheckUse::EventStream {
2603 expected_res: ExpectedResult::Ok,
2604 path: "/event/stream".parse().unwrap(),
2605 scope: vec![ComponentEventRoute {
2606 component: "/".to_string(),
2607 scope: Some(vec!["c".to_string()]),
2608 }],
2609 name: "started".parse().unwrap(),
2610 },
2611 )
2612 .await;
2613 }
2614
2615 pub async fn test_use_event_stream_from_above_root(&self) {
2621 let components = vec![(
2622 "a",
2623 ComponentDeclBuilder::new()
2624 .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2625 .build(),
2626 )];
2627
2628 let mut builder = T::new("a", components);
2629 builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2630 name: "started".parse().unwrap(),
2631 })]);
2632
2633 let model = builder.build().await;
2634 model
2635 .check_use(
2636 Moniker::root(),
2637 CheckUse::EventStream {
2638 expected_res: ExpectedResult::Ok,
2639 path: "/event/stream".parse().unwrap(),
2640 scope: vec![],
2641 name: "started".parse().unwrap(),
2642 },
2643 )
2644 .await;
2645 }
2646
2647 pub async fn test_use_event_stream_from_above_root_and_downscoped(&self) {
2656 let components = vec![
2657 (
2658 "a",
2659 ComponentDeclBuilder::new()
2660 .offer(
2661 OfferBuilder::event_stream()
2662 .name("started")
2663 .source(OfferSource::Parent)
2664 .target(OfferTarget::Child(ChildRef {
2665 name: "b".parse().unwrap(),
2666 collection: None,
2667 }))
2668 .scope(vec![
2669 EventScope::Child(ChildRef {
2670 name: "b".parse().unwrap(),
2671 collection: None,
2672 }),
2673 EventScope::Child(ChildRef {
2674 name: "c".parse().unwrap(),
2675 collection: None,
2676 }),
2677 ]),
2678 )
2679 .offer(
2680 OfferBuilder::event_stream()
2681 .name("started")
2682 .source(OfferSource::Parent)
2683 .target(OfferTarget::Child(ChildRef {
2684 name: "c".parse().unwrap(),
2685 collection: None,
2686 }))
2687 .scope(vec![
2688 EventScope::Child(ChildRef {
2689 name: "b".parse().unwrap(),
2690 collection: None,
2691 }),
2692 EventScope::Child(ChildRef {
2693 name: "c".parse().unwrap(),
2694 collection: None,
2695 }),
2696 ]),
2697 )
2698 .child_default("b")
2699 .child_default("c")
2700 .build(),
2701 ),
2702 (
2703 "b",
2704 ComponentDeclBuilder::new()
2705 .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2706 .build(),
2707 ),
2708 (
2709 "c",
2710 ComponentDeclBuilder::new()
2711 .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2712 .offer(
2713 OfferBuilder::event_stream()
2714 .name("started")
2715 .source(OfferSource::Parent)
2716 .target(OfferTarget::Child(ChildRef {
2717 name: "d".parse().unwrap(),
2718 collection: None,
2719 }))
2720 .scope(vec![EventScope::Child(ChildRef {
2721 name: "e".parse().unwrap(),
2722 collection: None,
2723 })]),
2724 )
2725 .child_default("d")
2726 .child_default("e")
2727 .build(),
2728 ),
2729 (
2730 "d",
2731 ComponentDeclBuilder::new()
2732 .use_(UseBuilder::event_stream().name("started").path("/event/stream"))
2733 .build(),
2734 ),
2735 ("e", ComponentDeclBuilder::new().build()),
2736 ];
2737
2738 let mut builder = T::new("a", components);
2739 builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2740 name: "started".parse().unwrap(),
2741 })]);
2742
2743 let model = builder.build().await;
2744 model
2745 .check_use(
2746 vec!["b"].try_into().unwrap(),
2747 CheckUse::EventStream {
2748 expected_res: ExpectedResult::Ok,
2749 path: "/event/stream".parse().unwrap(),
2750 scope: vec![ComponentEventRoute {
2751 component: "/".to_string(),
2752 scope: Some(vec!["b".to_string(), "c".to_string()]),
2753 }],
2754 name: "started".parse().unwrap(),
2755 },
2756 )
2757 .await;
2758 model
2759 .check_use(
2760 vec!["c"].try_into().unwrap(),
2761 CheckUse::EventStream {
2762 expected_res: ExpectedResult::Ok,
2763 path: "/event/stream".parse().unwrap(),
2764 scope: vec![ComponentEventRoute {
2765 component: "/".to_string(),
2766 scope: Some(vec!["b".to_string(), "c".to_string()]),
2767 }],
2768 name: "started".parse().unwrap(),
2769 },
2770 )
2771 .await;
2772 model
2773 .check_use(
2774 vec!["c", "d"].try_into().unwrap(), CheckUse::EventStream {
2776 expected_res: ExpectedResult::Ok,
2777 path: "/event/stream".parse().unwrap(),
2778 scope: vec![
2779 ComponentEventRoute {
2780 component: "/".to_string(),
2781 scope: Some(vec!["b".to_string(), "c".to_string()]),
2782 },
2783 ComponentEventRoute {
2784 component: "c".to_string(),
2785 scope: Some(vec!["e".to_string()]),
2786 },
2787 ],
2788 name: "started".parse().unwrap(),
2789 },
2790 )
2791 .await;
2792 }
2793
2794 pub async fn test_can_offer_capability_requested_event(&self) {
2800 let components = vec![
2801 (
2802 "a",
2803 ComponentDeclBuilder::new()
2804 .offer(
2805 OfferBuilder::event_stream()
2806 .name("capability_requested")
2807 .target_name("capability_requested_on_a")
2808 .source(OfferSource::Parent)
2809 .target_static_child("b"),
2810 )
2811 .child_default("b")
2812 .build(),
2813 ),
2814 (
2815 "b",
2816 ComponentDeclBuilder::new()
2817 .use_(UseBuilder::event_stream().name("capability_requested_on_a"))
2818 .build(),
2819 ),
2820 ];
2821
2822 let mut builder = T::new("a", components);
2823 builder.set_builtin_capabilities(vec![CapabilityDecl::EventStream(EventStreamDecl {
2824 name: "capability_requested".parse().unwrap(),
2825 })]);
2826 let model = builder.build().await;
2827
2828 model
2829 .check_use(
2830 vec!["b"].try_into().unwrap(),
2831 CheckUse::EventStream {
2832 expected_res: ExpectedResult::Ok,
2833 path: "/svc/fuchsia.component.EventStream".parse().unwrap(),
2834 scope: vec![ComponentEventRoute { component: "/".to_string(), scope: None }],
2835 name: "capability_requested_on_a".parse().unwrap(),
2836 },
2837 )
2838 .await;
2839 }
2840
2841 pub async fn test_use_protocol_denied_by_capability_policy(&self) {
2848 let components = vec![
2849 (
2850 "a",
2851 ComponentDeclBuilder::new()
2852 .protocol_default("hippo")
2853 .offer(
2854 OfferBuilder::protocol()
2855 .name("hippo")
2856 .source(OfferSource::Self_)
2857 .target_static_child("b"),
2858 )
2859 .child_default("b")
2860 .build(),
2861 ),
2862 ("b", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
2863 ];
2864 let mut builder = T::new("a", components);
2865 builder.add_capability_policy(
2866 CapabilityAllowlistKey {
2867 source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
2868 source_name: "hippo".parse().unwrap(),
2869 source: CapabilityAllowlistSource::Self_,
2870 capability: CapabilityTypeName::Protocol,
2871 },
2872 HashSet::new(),
2873 );
2874
2875 let model = builder.build().await;
2876 model
2877 .check_use(
2878 vec!["b"].try_into().unwrap(),
2879 CheckUse::Protocol {
2880 path: default_service_capability(),
2881 expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
2882 },
2883 )
2884 .await;
2885 }
2886
2887 pub async fn test_use_directory_with_alias_denied_by_capability_policy(&self) {
2894 let components = vec![
2895 (
2896 "a",
2897 ComponentDeclBuilder::new()
2898 .capability(CapabilityBuilder::directory().name("foo_data").path("/data/foo"))
2899 .offer(
2900 OfferBuilder::directory()
2901 .name("foo_data")
2902 .target_name("bar_data")
2903 .source(OfferSource::Self_)
2904 .target_static_child("b")
2905 .rights(fio::R_STAR_DIR),
2906 )
2907 .child_default("b")
2908 .build(),
2909 ),
2910 (
2911 "b",
2912 ComponentDeclBuilder::new()
2913 .use_(UseBuilder::directory().name("bar_data").path("/data/hippo"))
2914 .build(),
2915 ),
2916 ];
2917 let mut builder = T::new("a", components);
2918 builder.add_capability_policy(
2919 CapabilityAllowlistKey {
2920 source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
2921 source_name: "foo_data".parse().unwrap(),
2922 source: CapabilityAllowlistSource::Self_,
2923 capability: CapabilityTypeName::Directory,
2924 },
2925 HashSet::new(),
2926 );
2927 let model = builder.build().await;
2928 model
2929 .check_use(
2930 vec!["b"].try_into().unwrap(),
2931 CheckUse::default_directory(ExpectedResult::Err(zx::Status::ACCESS_DENIED)),
2932 )
2933 .await;
2934 }
2935
2936 pub async fn test_use_protocol_partial_chain_allowed_by_capability_policy(&self) {
2946 let components = vec![
2947 (
2948 "a",
2949 ComponentDeclBuilder::new()
2950 .protocol_default("hippo")
2951 .offer(
2952 OfferBuilder::protocol()
2953 .name("hippo")
2954 .source(OfferSource::Self_)
2955 .target_static_child("b"),
2956 )
2957 .child_default("b")
2958 .build(),
2959 ),
2960 (
2961 "b",
2962 ComponentDeclBuilder::new()
2963 .offer(
2964 OfferBuilder::protocol()
2965 .name("hippo")
2966 .source(OfferSource::Parent)
2967 .target_static_child("c"),
2968 )
2969 .use_(UseBuilder::protocol().name("hippo"))
2970 .child_default("c")
2971 .build(),
2972 ),
2973 ("c", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
2974 ];
2975
2976 let mut allowlist = HashSet::new();
2977 allowlist.insert(AllowlistEntryBuilder::new().exact("b").build());
2978
2979 let mut builder = T::new("a", components);
2980 builder.add_capability_policy(
2981 CapabilityAllowlistKey {
2982 source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
2983 source_name: "hippo".parse().unwrap(),
2984 source: CapabilityAllowlistSource::Self_,
2985 capability: CapabilityTypeName::Protocol,
2986 },
2987 allowlist,
2988 );
2989 let model = builder.build().await;
2990
2991 model
2992 .check_use(
2993 vec!["b"].try_into().unwrap(),
2994 CheckUse::Protocol {
2995 path: default_service_capability(),
2996 expected_res: ExpectedResult::Ok,
2997 },
2998 )
2999 .await;
3000
3001 model
3002 .check_use(
3003 vec!["b", "c"].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_protocol_component_provided_capability_policy(&self) {
3022 let components = vec![
3023 (
3024 "a",
3025 ComponentDeclBuilder::new()
3026 .protocol_default("hippo")
3027 .offer(
3028 OfferBuilder::protocol()
3029 .name("hippo")
3030 .source(OfferSource::Self_)
3031 .target_static_child("b"),
3032 )
3033 .child_default("b")
3034 .build(),
3035 ),
3036 (
3037 "b",
3038 ComponentDeclBuilder::new()
3039 .offer(
3040 OfferBuilder::protocol()
3041 .name("hippo")
3042 .source(OfferSource::Parent)
3043 .target_static_child("c"),
3044 )
3045 .offer(
3046 OfferBuilder::protocol()
3047 .name("hippo")
3048 .source(OfferSource::Parent)
3049 .target_static_child("d"),
3050 )
3051 .child_default("c")
3052 .child_default("d")
3053 .build(),
3054 ),
3055 ("c", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
3056 ("d", ComponentDeclBuilder::new().use_(UseBuilder::protocol().name("hippo")).build()),
3057 ];
3058
3059 let mut allowlist = HashSet::new();
3060 allowlist.insert(AllowlistEntryBuilder::new().exact("b").build());
3061 allowlist.insert(AllowlistEntryBuilder::new().exact("b").exact("c").build());
3062
3063 let mut builder = T::new("a", components);
3064 builder.add_capability_policy(
3065 CapabilityAllowlistKey {
3066 source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
3067 source_name: "hippo".parse().unwrap(),
3068 source: CapabilityAllowlistSource::Self_,
3069 capability: CapabilityTypeName::Protocol,
3070 },
3071 allowlist,
3072 );
3073 let model = builder.build().await;
3074
3075 model
3076 .check_use(
3077 vec!["b", "c"].try_into().unwrap(),
3078 CheckUse::Protocol {
3079 path: default_service_capability(),
3080 expected_res: ExpectedResult::Ok,
3081 },
3082 )
3083 .await;
3084
3085 model
3086 .check_use(
3087 vec!["b", "d"].try_into().unwrap(),
3088 CheckUse::Protocol {
3089 path: default_service_capability(),
3090 expected_res: ExpectedResult::Err(zx::Status::ACCESS_DENIED),
3091 },
3092 )
3093 .await;
3094 }
3095
3096 pub async fn test_use_from_component_manager_namespace_denied_by_policy(&self) {
3102 let components = vec![(
3103 "a",
3104 ComponentDeclBuilder::new()
3105 .use_(UseBuilder::protocol().name("foo").path("/svc/hippo"))
3106 .build(),
3107 )];
3108 let namespace_capabilities = vec![CapabilityBuilder::protocol()
3109 .name("foo")
3110 .path("/use_from_cm_namespace/svc/foo")
3111 .build()];
3112 let mut builder = T::new("a", components);
3113 builder.set_namespace_capabilities(namespace_capabilities);
3114 builder.add_capability_policy(
3115 CapabilityAllowlistKey {
3116 source_moniker: ExtendedMoniker::ComponentManager,
3117 source_name: "foo".parse().unwrap(),
3118 source: CapabilityAllowlistSource::Self_,
3119 capability: CapabilityTypeName::Protocol,
3120 },
3121 HashSet::new(),
3122 );
3123 let model = builder.build().await;
3124
3125 model.install_namespace_directory("/use_from_cm_namespace");
3126 model
3127 .check_use(
3128 Moniker::root(),
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_route_service_from_parent(&self) {
3144 let use_decl = UseBuilder::service().name("foo").build();
3145 let components = vec![
3146 (
3147 "a",
3148 ComponentDeclBuilder::new()
3149 .offer(
3150 OfferBuilder::service()
3151 .name("foo")
3152 .source(OfferSource::Self_)
3153 .target_static_child("b"),
3154 )
3155 .service_default("foo")
3156 .child_default("b")
3157 .build(),
3158 ),
3159 ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3160 ];
3161 let model = T::new("a", components).build().await;
3162 let b_component =
3163 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3164 let a_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
3165 let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3166 let source = route_capability(
3167 RouteRequest::UseService(use_decl),
3168 &b_component,
3169 &mut NoopRouteMapper,
3170 )
3171 .await
3172 .expect("failed to route service");
3173 match source {
3174 RouteSource {
3175 source:
3176 CapabilitySource::Component(ComponentSource {
3177 capability: ComponentCapability::Service(ServiceDecl { name, source_path }),
3178 moniker,
3179 }),
3180 relative_path,
3181 } if relative_path.is_dot() => {
3182 assert_eq!(name, "foo");
3183 assert_eq!(
3184 source_path.expect("missing source path"),
3185 "/svc/foo".parse::<cm_types::Path>().unwrap()
3186 );
3187 assert_eq!(&moniker, a_component.moniker());
3188 }
3189 _ => panic!("bad capability source"),
3190 };
3191 }
3192
3193 pub async fn test_route_service_from_child(&self) {
3200 let use_decl = UseBuilder::service().name("foo").source_static_child("b").build();
3201 let components = vec![
3202 ("a", ComponentDeclBuilder::new().use_(use_decl.clone()).child_default("b").build()),
3203 (
3204 "b",
3205 ComponentDeclBuilder::new()
3206 .service_default("foo")
3207 .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3208 .build(),
3209 ),
3210 ];
3211 let model = T::new("a", components).build().await;
3212 let a_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
3213 let b_component =
3214 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3215 let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3216 let source = route_capability(
3217 RouteRequest::UseService(use_decl),
3218 &a_component,
3219 &mut NoopRouteMapper,
3220 )
3221 .await
3222 .expect("failed to route service");
3223 match source {
3224 RouteSource {
3225 source:
3226 CapabilitySource::Component(ComponentSource {
3227 capability: ComponentCapability::Service(ServiceDecl { name, source_path }),
3228 moniker,
3229 }),
3230 relative_path,
3231 } if relative_path.is_dot() => {
3232 assert_eq!(name, "foo");
3233 assert_eq!(
3234 source_path.expect("missing source path"),
3235 "/svc/foo".parse::<cm_types::Path>().unwrap()
3236 );
3237 assert_eq!(&moniker, b_component.moniker());
3238 }
3239 _ => panic!("bad capability source"),
3240 };
3241 }
3242
3243 pub async fn test_route_service_from_sibling(&self) {
3251 let use_decl = UseBuilder::service().name("foo").build();
3252 let components = vec![
3253 (
3254 "a",
3255 ComponentDeclBuilder::new()
3256 .offer(
3257 OfferBuilder::service()
3258 .name("foo")
3259 .source_static_child("c")
3260 .target_static_child("b"),
3261 )
3262 .child_default("b")
3263 .child_default("c")
3264 .build(),
3265 ),
3266 ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3267 (
3268 "c",
3269 ComponentDeclBuilder::new()
3270 .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3271 .service_default("foo")
3272 .build(),
3273 ),
3274 ];
3275 let model = T::new("a", components).build().await;
3276 let b_component =
3277 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3278 let c_component =
3279 model.look_up_instance(&vec!["c"].try_into().unwrap()).await.expect("c instance");
3280 let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3281 let source = route_capability(
3282 RouteRequest::UseService(use_decl),
3283 &b_component,
3284 &mut NoopRouteMapper,
3285 )
3286 .await
3287 .expect("failed to route service");
3288
3289 match source {
3291 RouteSource {
3292 source:
3293 CapabilitySource::Component(ComponentSource {
3294 capability: ComponentCapability::Service(ServiceDecl { name, source_path }),
3295 moniker,
3296 }),
3297 relative_path,
3298 } if relative_path.is_dot() => {
3299 assert_eq!(name, "foo");
3300 assert_eq!(
3301 source_path.expect("missing source path"),
3302 "/svc/foo".parse::<cm_types::Path>().unwrap()
3303 );
3304 assert_eq!(&moniker, c_component.moniker());
3305 }
3306 _ => panic!("bad capability source"),
3307 };
3308 }
3309
3310 pub async fn test_route_filtered_service_from_sibling(&self) {
3318 let use_decl = UseBuilder::service().name("foo").build();
3319 let components = vec![
3320 (
3321 "a",
3322 ComponentDeclBuilder::new()
3323 .offer(
3324 OfferBuilder::service()
3325 .name("foo")
3326 .source_static_child("c")
3327 .target_static_child("b")
3328 .source_instance_filter(["service_instance_0"])
3329 .renamed_instances([]),
3330 )
3331 .child_default("b")
3332 .child_default("c")
3333 .build(),
3334 ),
3335 ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3336 (
3337 "c",
3338 ComponentDeclBuilder::new()
3339 .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3340 .service_default("foo")
3341 .build(),
3342 ),
3343 ];
3344 let model = T::new("a", components).build().await;
3345 let b_component =
3346 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3347 let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3348 let source = route_capability(
3349 RouteRequest::UseService(use_decl),
3350 &b_component,
3351 &mut NoopRouteMapper,
3352 )
3353 .await
3354 .expect("failed to route service");
3355
3356 let RouteSource { source, relative_path } = source;
3358 assert!(relative_path.is_dot(), "unexpected capability path");
3359 assert_eq!(source.source_name(), Some(&"foo".parse().unwrap()));
3360 assert_eq!(
3361 source.source_moniker(),
3362 ExtendedMoniker::ComponentInstance("c".parse().unwrap())
3363 );
3364 }
3365
3366 pub async fn test_route_renamed_service_instance_from_sibling(&self) {
3374 let use_decl = UseBuilder::service().name("foo").build();
3375 let components = vec![
3376 (
3377 "a",
3378 ComponentDeclBuilder::new()
3379 .offer(
3380 OfferBuilder::service()
3381 .name("foo")
3382 .source_static_child("c")
3383 .target_static_child("b")
3384 .renamed_instances([("instance_0", "renamed_instance_0")]),
3385 )
3386 .child_default("b")
3387 .child_default("c")
3388 .build(),
3389 ),
3390 ("b", ComponentDeclBuilder::new().use_(use_decl.clone()).build()),
3391 (
3392 "c",
3393 ComponentDeclBuilder::new()
3394 .expose(ExposeBuilder::service().name("foo").source(ExposeSource::Self_))
3395 .service_default("foo")
3396 .build(),
3397 ),
3398 ];
3399 let model = T::new("a", components).build().await;
3400 let b_component =
3401 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3402 let UseDecl::Service(use_decl) = use_decl else { unreachable!() };
3403 let source = route_capability(
3404 RouteRequest::UseService(use_decl),
3405 &b_component,
3406 &mut NoopRouteMapper,
3407 )
3408 .await
3409 .expect("failed to route service");
3410
3411 let RouteSource { source, relative_path } = source;
3413 assert!(relative_path.is_dot(), "unexpected capability path");
3414 assert_eq!(source.source_name(), Some(&"foo".parse().unwrap()));
3415 assert_eq!(
3416 source.source_moniker(),
3417 ExtendedMoniker::ComponentInstance("c".parse().unwrap())
3418 );
3419 }
3420
3421 pub async fn test_route_runner_from_parent_environment(&self) {
3429 let components = vec![
3430 (
3431 "a",
3432 ComponentDeclBuilder::new()
3433 .child(ChildBuilder::new().name("b").environment("env"))
3434 .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3435 source_name: "elf".parse().unwrap(),
3436 source: RegistrationSource::Self_,
3437 target_name: "hobbit".parse().unwrap(),
3438 }))
3439 .runner_default("elf")
3440 .build(),
3441 ),
3442 ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3443 ];
3444
3445 let model = T::new("a", components).build().await;
3446 let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3447 let b_component =
3448 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3449 let source = route_capability(
3450 RouteRequest::UseRunner(UseRunnerDecl {
3451 source: UseSource::Environment,
3452 source_name: "hobbit".parse().unwrap(),
3453 source_dictionary: Default::default(),
3454 }),
3455 &b_component,
3456 &mut NoopRouteMapper,
3457 )
3458 .await
3459 .expect("failed to route runner");
3460
3461 match source {
3463 RouteSource {
3464 source:
3465 CapabilitySource::Component(ComponentSource {
3466 capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3467 moniker,
3468 }),
3469 relative_path,
3470 } if relative_path.is_dot() => {
3471 assert_eq!(name, "elf");
3472 assert_eq!(
3473 source_path.expect("missing source path"),
3474 format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3475 .parse::<cm_types::Path>()
3476 .unwrap()
3477 );
3478 assert_eq!(&moniker, a_component.moniker());
3479 }
3480 _ => panic!("bad capability source"),
3481 };
3482 }
3483
3484 pub async fn test_route_runner_from_grandparent_environment(&self) {
3495 let components = vec![
3496 (
3497 "a",
3498 ComponentDeclBuilder::new()
3499 .child_default("b")
3500 .offer(
3501 OfferBuilder::runner()
3502 .name("elf")
3503 .target_name("dwarf")
3504 .source(OfferSource::Self_)
3505 .target_static_child("b"),
3506 )
3507 .runner_default("elf")
3508 .build(),
3509 ),
3510 (
3511 "b",
3512 ComponentDeclBuilder::new()
3513 .child(ChildBuilder::new().name("c").environment("env"))
3514 .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3515 source_name: "dwarf".parse().unwrap(),
3516 source: RegistrationSource::Parent,
3517 target_name: "hobbit".parse().unwrap(),
3518 }))
3519 .build(),
3520 ),
3521 ("c", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3522 ];
3523
3524 let model = T::new("a", components).build().await;
3525 let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3526 let c_component =
3527 model.look_up_instance(&vec!["b", "c"].try_into().unwrap()).await.expect("c instance");
3528 let source = route_capability(
3529 RouteRequest::UseRunner(UseRunnerDecl {
3530 source: UseSource::Environment,
3531 source_name: "hobbit".parse().unwrap(),
3532 source_dictionary: Default::default(),
3533 }),
3534 &c_component,
3535 &mut NoopRouteMapper,
3536 )
3537 .await
3538 .expect("failed to route runner");
3539
3540 match source {
3542 RouteSource {
3543 source:
3544 CapabilitySource::Component(ComponentSource {
3545 capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3546 moniker,
3547 }),
3548 relative_path,
3549 } if relative_path.is_dot() => {
3550 assert_eq!(name, "elf");
3551 assert_eq!(
3552 source_path.expect("missing source path"),
3553 format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3554 .parse::<cm_types::Path>()
3555 .unwrap()
3556 );
3557 assert_eq!(&moniker, a_component.moniker());
3558 }
3559 _ => panic!("bad capability source"),
3560 };
3561 }
3562
3563 pub async fn test_route_runner_from_sibling_environment(&self) {
3571 let components = vec![
3572 (
3573 "a",
3574 ComponentDeclBuilder::new()
3575 .child_default("b")
3576 .child(ChildBuilder::new().name("c").environment("env"))
3577 .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3578 source_name: "dwarf".parse().unwrap(),
3579 source: RegistrationSource::Child("b".parse().unwrap()),
3580 target_name: "hobbit".parse().unwrap(),
3581 }))
3582 .build(),
3583 ),
3584 (
3585 "b",
3586 ComponentDeclBuilder::new()
3587 .expose(
3588 ExposeBuilder::runner()
3589 .name("elf")
3590 .target_name("dwarf")
3591 .source(ExposeSource::Self_),
3592 )
3593 .runner_default("elf")
3594 .build(),
3595 ),
3596 ("c", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3597 ];
3598
3599 let model = T::new("a", components).build().await;
3600 let b_component =
3601 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3602 let c_component =
3603 model.look_up_instance(&vec!["c"].try_into().unwrap()).await.expect("c instance");
3604 let source = route_capability(
3605 RouteRequest::UseRunner(UseRunnerDecl {
3606 source: UseSource::Environment,
3607 source_name: "hobbit".parse().unwrap(),
3608 source_dictionary: Default::default(),
3609 }),
3610 &c_component,
3611 &mut NoopRouteMapper,
3612 )
3613 .await
3614 .expect("failed to route runner");
3615
3616 match source {
3618 RouteSource {
3619 source:
3620 CapabilitySource::Component(ComponentSource {
3621 capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3622 moniker,
3623 }),
3624 relative_path,
3625 } if relative_path.is_dot() => {
3626 assert_eq!(name, "elf");
3627 assert_eq!(
3628 source_path.expect("missing source path"),
3629 format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3630 .parse::<cm_types::Path>()
3631 .unwrap()
3632 );
3633 assert_eq!(&moniker, b_component.moniker());
3634 }
3635 _ => panic!("bad capability source"),
3636 };
3637 }
3638
3639 pub async fn test_route_runner_from_inherited_environment(&self) {
3650 let components = vec![
3651 (
3652 "a",
3653 ComponentDeclBuilder::new()
3654 .child(ChildBuilder::new().name("b").environment("env"))
3655 .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3656 source_name: "elf".parse().unwrap(),
3657 source: RegistrationSource::Self_,
3658 target_name: "hobbit".parse().unwrap(),
3659 }))
3660 .runner_default("elf")
3661 .build(),
3662 ),
3663 (
3664 "b",
3665 ComponentDeclBuilder::new()
3666 .child(ChildBuilder::new().name("c").environment("env"))
3667 .environment(EnvironmentBuilder::new().name("env"))
3668 .build(),
3669 ),
3670 ("c", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3671 ];
3672
3673 let model = T::new("a", components).build().await;
3674 let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3675 let c_component =
3676 model.look_up_instance(&vec!["b", "c"].try_into().unwrap()).await.expect("c instance");
3677 let source = route_capability(
3678 RouteRequest::UseRunner(UseRunnerDecl {
3679 source: UseSource::Environment,
3680 source_name: "hobbit".parse().unwrap(),
3681 source_dictionary: Default::default(),
3682 }),
3683 &c_component,
3684 &mut NoopRouteMapper,
3685 )
3686 .await
3687 .expect("failed to route runner");
3688
3689 match source {
3691 RouteSource {
3692 source:
3693 CapabilitySource::Component(ComponentSource {
3694 capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
3695 moniker,
3696 }),
3697 relative_path,
3698 } if relative_path.is_dot() => {
3699 assert_eq!(name, "elf");
3700 assert_eq!(
3701 source_path.expect("missing source path"),
3702 format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
3703 .parse::<cm_types::Path>()
3704 .unwrap()
3705 );
3706 assert_eq!(&moniker, a_component.moniker());
3707 }
3708 _ => panic!("bad capability source"),
3709 };
3710 }
3711
3712 pub async fn test_route_runner_from_environment_not_found(&self) {
3720 let components = vec![
3721 (
3722 "a",
3723 ComponentDeclBuilder::new()
3724 .child(ChildBuilder::new().name("b").environment("env"))
3725 .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3726 source_name: "elf".parse().unwrap(),
3727 source: RegistrationSource::Self_,
3728 target_name: "dwarf".parse().unwrap(),
3729 }))
3730 .runner_default("elf")
3731 .build(),
3732 ),
3733 ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3734 ];
3735
3736 let model = T::new("a", components).build().await;
3737 let b_component =
3738 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3739 let route_result = route_capability(
3740 RouteRequest::UseRunner(UseRunnerDecl {
3741 source: UseSource::Environment,
3742 source_name: "hobbit".parse().unwrap(),
3743 source_dictionary: Default::default(),
3744 }),
3745 &b_component,
3746 &mut NoopRouteMapper,
3747 )
3748 .await;
3749
3750 assert_matches!(
3751 route_result,
3752 Err(RoutingError::UseFromEnvironmentNotFound {
3753 moniker,
3754 capability_type,
3755 capability_name,
3756 }
3757 )
3758 if moniker == *b_component.moniker() &&
3759 capability_type == "runner" &&
3760 capability_name == "hobbit"
3761 );
3762 }
3763
3764 pub async fn test_route_builtin_runner(&self) {
3771 let components = vec![
3772 (
3773 "a",
3774 ComponentDeclBuilder::new()
3775 .child(ChildBuilder::new().name("b").environment("env"))
3776 .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3777 source_name: "elf".parse().unwrap(),
3778 source: RegistrationSource::Parent,
3779 target_name: "hobbit".parse().unwrap(),
3780 }))
3781 .build(),
3782 ),
3783 ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3784 ];
3785
3786 let mut builder = T::new("a", components);
3787 builder.set_builtin_capabilities(vec![CapabilityDecl::Runner(RunnerDecl {
3788 name: "elf".parse().unwrap(),
3789 source_path: None,
3790 })]);
3791 builder.register_mock_builtin_runner("elf");
3792 let model = builder.build().await;
3793
3794 let b_component =
3795 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3796 let source = route_capability(
3797 RouteRequest::UseRunner(UseRunnerDecl {
3798 source: UseSource::Environment,
3799 source_name: "hobbit".parse().unwrap(),
3800 source_dictionary: Default::default(),
3801 }),
3802 &b_component,
3803 &mut NoopRouteMapper,
3804 )
3805 .await
3806 .expect("failed to route runner");
3807
3808 match source {
3810 RouteSource {
3811 source:
3812 CapabilitySource::Builtin(BuiltinSource {
3813 capability: InternalCapability::Runner(name),
3814 ..
3815 }),
3816 relative_path,
3817 } if relative_path.is_dot() => {
3818 assert_eq!(name, "elf");
3819 }
3820 _ => panic!("bad capability source"),
3821 };
3822 }
3823
3824 pub async fn test_route_builtin_runner_from_root_env(&self) {
3828 let use_runner_decl =
3829 UseBuilder::runner().source(UseSource::Environment).name("elf").build();
3830 let components = vec![(
3831 "a",
3832 ComponentDeclBuilder::new_empty_component().use_(use_runner_decl.clone()).build(),
3833 )];
3834
3835 let mut builder = T::new("a", components);
3836 builder.set_builtin_capabilities(vec![CapabilityDecl::Runner(RunnerDecl {
3837 name: "elf".parse().unwrap(),
3838 source_path: None,
3839 })]);
3840 builder.register_mock_builtin_runner("elf");
3841 let model = builder.build().await;
3842
3843 let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3844 let source = route_capability(
3845 RouteRequest::UseRunner(match use_runner_decl {
3846 UseDecl::Runner(u) => u,
3847 _ => panic!("unexpected use type"),
3848 }),
3849 &a_component,
3850 &mut NoopRouteMapper,
3851 )
3852 .await
3853 .expect("failed to route runner");
3854
3855 match source {
3857 RouteSource {
3858 source:
3859 CapabilitySource::Builtin(BuiltinSource {
3860 capability: InternalCapability::Runner(name),
3861 ..
3862 }),
3863 relative_path,
3864 } if relative_path.is_dot() => {
3865 assert_eq!(name, "elf");
3866 }
3867 _ => panic!("bad capability source"),
3868 };
3869 }
3870
3871 pub async fn test_route_builtin_runner_not_found(&self) {
3879 let components = vec![
3880 (
3881 "a",
3882 ComponentDeclBuilder::new()
3883 .child(ChildBuilder::new().name("b").environment("env"))
3884 .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
3885 source_name: "elf".parse().unwrap(),
3886 source: RegistrationSource::Parent,
3887 target_name: "hobbit".parse().unwrap(),
3888 }))
3889 .build(),
3890 ),
3891 ("b", ComponentDeclBuilder::new_empty_component().program_runner("hobbit").build()),
3892 ];
3893
3894 let mut builder = T::new("a", components);
3895 builder.register_mock_builtin_runner("elf");
3896
3897 let model = builder.build().await;
3898 let b_component =
3899 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3900 let route_result = route_capability(
3901 RouteRequest::UseRunner(UseRunnerDecl {
3902 source: UseSource::Environment,
3903 source_name: "hobbit".parse().unwrap(),
3904 source_dictionary: Default::default(),
3905 }),
3906 &b_component,
3907 &mut NoopRouteMapper,
3908 )
3909 .await;
3910
3911 assert_matches!(
3912 route_result,
3913 Err(RoutingError::RegisterFromComponentManagerNotFound {
3914 capability_id,
3915 }
3916 )
3917 if capability_id == "elf".to_string()
3918 );
3919 }
3920
3921 pub async fn test_route_builtin_runner_from_root_env_registration_not_found(&self) {
3927 let use_runner_decl = UseRunnerDecl {
3928 source: UseSource::Environment,
3929 source_name: "hobbit".parse().unwrap(),
3930 source_dictionary: Default::default(),
3931 };
3932 let components = vec![(
3933 "a",
3934 ComponentDeclBuilder::new_empty_component().use_(use_runner_decl.clone()).build(),
3935 )];
3936
3937 let mut builder = T::new("a", components);
3938 builder.set_builtin_capabilities(vec![CapabilityDecl::Runner(RunnerDecl {
3939 name: "hobbit".parse().unwrap(),
3940 source_path: None,
3941 })]);
3942 let model = builder.build().await;
3943
3944 let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3945 let route_result = route_capability(
3946 RouteRequest::UseRunner(use_runner_decl),
3947 &a_component,
3948 &mut NoopRouteMapper,
3949 )
3950 .await;
3951
3952 assert_matches!(
3953 route_result,
3954 Err(RoutingError::UseFromEnvironmentNotFound {
3955 moniker,
3956 capability_type,
3957 capability_name,
3958 }
3959 )
3960 if moniker == *a_component.moniker()
3961 && capability_type == "runner".to_string()
3962 && capability_name == "hobbit"
3963 );
3964 }
3965
3966 pub async fn test_use_runner_from_child(&self) {
3973 let components = vec![
3974 (
3975 "a",
3976 ComponentDeclBuilder::new_empty_component()
3977 .use_(UseBuilder::runner().source_static_child("b").name("dwarf"))
3978 .child_default("b")
3979 .build(),
3980 ),
3981 (
3982 "b",
3983 ComponentDeclBuilder::new()
3984 .expose(
3985 ExposeBuilder::runner()
3986 .name("elf")
3987 .target_name("dwarf")
3988 .source(ExposeSource::Self_),
3989 )
3990 .runner_default("elf")
3991 .build(),
3992 ),
3993 ];
3994
3995 let model = T::new("a", components).build().await;
3996 let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
3997 let b_component =
3998 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
3999 let source = route_capability(
4000 RouteRequest::UseRunner(UseRunnerDecl {
4001 source: UseSource::Child("b".parse().unwrap()),
4002 source_name: "dwarf".parse().unwrap(),
4003 source_dictionary: Default::default(),
4004 }),
4005 &a_component,
4006 &mut NoopRouteMapper,
4007 )
4008 .await
4009 .expect("failed to route runner");
4010
4011 match source {
4013 RouteSource {
4014 source:
4015 CapabilitySource::Component(ComponentSource {
4016 capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
4017 moniker,
4018 }),
4019 relative_path,
4020 } if relative_path.is_dot() => {
4021 assert_eq!(name, "elf");
4022 assert_eq!(
4023 source_path.expect("missing source path"),
4024 format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
4025 .parse::<cm_types::Path>()
4026 .unwrap()
4027 );
4028 assert_eq!(&moniker, b_component.moniker());
4029 }
4030 _ => panic!("bad capability source"),
4031 };
4032 }
4033
4034 pub async fn test_use_runner_from_parent(&self) {
4041 let components = vec![
4042 (
4043 "a",
4044 ComponentDeclBuilder::new()
4045 .offer(
4046 OfferBuilder::runner()
4047 .name("elf")
4048 .target_name("dwarf")
4049 .source(OfferSource::Self_)
4050 .target_static_child("b"),
4051 )
4052 .runner_default("elf")
4053 .child_default("b")
4054 .build(),
4055 ),
4056 (
4057 "b",
4058 ComponentDeclBuilder::new_empty_component()
4059 .use_(UseBuilder::runner().name("dwarf"))
4060 .build(),
4061 ),
4062 ];
4063
4064 let model = T::new("a", components).build().await;
4065 let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
4066 let b_component =
4067 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
4068 let source = route_capability(
4069 RouteRequest::UseRunner(UseRunnerDecl {
4070 source: UseSource::Parent,
4071 source_name: "dwarf".parse().unwrap(),
4072 source_dictionary: Default::default(),
4073 }),
4074 &b_component,
4075 &mut NoopRouteMapper,
4076 )
4077 .await
4078 .expect("failed to route runner");
4079
4080 match source {
4082 RouteSource {
4083 source:
4084 CapabilitySource::Component(ComponentSource {
4085 capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
4086 moniker,
4087 }),
4088 relative_path,
4089 } if relative_path.is_dot() => {
4090 assert_eq!(name, "elf");
4091 assert_eq!(
4092 source_path.expect("missing source path"),
4093 format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
4094 .parse::<cm_types::Path>()
4095 .unwrap()
4096 );
4097 assert_eq!(&moniker, a_component.moniker());
4098 }
4099 _ => panic!("bad capability source"),
4100 };
4101 }
4102
4103 pub async fn test_use_runner_from_parent_environment(&self) {
4111 let components = vec![
4112 (
4113 "a",
4114 ComponentDeclBuilder::new()
4115 .child(ChildBuilder::new().name("b").environment("env"))
4116 .environment(EnvironmentBuilder::new().name("env").runner(RunnerRegistration {
4117 source_name: "elf".parse().unwrap(),
4118 source: RegistrationSource::Self_,
4119 target_name: "hobbit".parse().unwrap(),
4120 }))
4121 .runner_default("elf")
4122 .build(),
4123 ),
4124 (
4125 "b",
4126 ComponentDeclBuilder::new_empty_component()
4127 .use_(UseBuilder::runner().source(UseSource::Environment).name("hobbit"))
4128 .build(),
4129 ),
4130 ];
4131
4132 let model = T::new("a", components).build().await;
4133 let a_component = model.look_up_instance(&Moniker::root()).await.expect("a instance");
4134 let b_component =
4135 model.look_up_instance(&vec!["b"].try_into().unwrap()).await.expect("b instance");
4136 let source = route_capability(
4137 RouteRequest::UseRunner(UseRunnerDecl {
4138 source: UseSource::Environment,
4139 source_name: "hobbit".parse().unwrap(),
4140 source_dictionary: Default::default(),
4141 }),
4142 &b_component,
4143 &mut NoopRouteMapper,
4144 )
4145 .await
4146 .expect("failed to route runner");
4147
4148 match source {
4150 RouteSource {
4151 source:
4152 CapabilitySource::Component(ComponentSource {
4153 capability: ComponentCapability::Runner(RunnerDecl { name, source_path }),
4154 moniker,
4155 }),
4156 relative_path,
4157 } if relative_path.is_dot() => {
4158 assert_eq!(name, "elf");
4159 assert_eq!(
4160 source_path.expect("missing source path"),
4161 format!("/svc/{}", fcrunner::ComponentRunnerMarker::DEBUG_NAME)
4162 .parse::<cm_types::Path>()
4163 .unwrap()
4164 );
4165 assert_eq!(&moniker, a_component.moniker());
4166 }
4167 _ => panic!("bad capability source"),
4168 };
4169 }
4170
4171 pub async fn test_use_config_from_self(&self) {
4178 let good_value = cm_rust::ConfigSingleValue::Int8(12);
4179 let use_config = UseBuilder::config()
4180 .source(cm_rust::UseSource::Self_)
4181 .name("fuchsia.MyConfig")
4182 .target_name("my_config")
4183 .config_type(cm_rust::ConfigValueType::Int8)
4184 .build();
4185 let components = vec![
4186 ("a", ComponentDeclBuilder::new().child_default("b").build()),
4187 (
4188 "b",
4189 ComponentDeclBuilder::new()
4190 .capability(
4191 CapabilityBuilder::config()
4192 .name("fuchsia.MyConfig")
4193 .value(good_value.clone().into()),
4194 )
4195 .use_(use_config.clone())
4196 .config(cm_rust::ConfigDecl {
4197 fields: vec![cm_rust::ConfigField {
4198 key: "my_config".into(),
4199 type_: cm_rust::ConfigValueType::Int8,
4200 mutability: Default::default(),
4201 }],
4202 checksum: cm_rust::ConfigChecksum::Sha256([0; 32]),
4203 value_source: cm_rust::ConfigValueSource::Capabilities(Default::default()),
4204 })
4205 .build(),
4206 ),
4207 ];
4208
4209 let model = T::new("a", components).build().await;
4210 let child_component = model.look_up_instance(&vec!["b"].try_into().unwrap()).await.unwrap();
4211
4212 let cm_rust::UseDecl::Config(use_config) = use_config else { panic!() };
4213 let value =
4214 routing::config::route_config_value(&use_config, &child_component).await.unwrap();
4215 assert_eq!(value, Some(cm_rust::ConfigValue::Single(good_value)));
4216 }
4217
4218 pub async fn test_use_config_from_parent(&self) {
4225 let good_value = cm_rust::ConfigSingleValue::Int8(12);
4226 let use_config = UseBuilder::config()
4227 .source(cm_rust::UseSource::Parent)
4228 .name("fuchsia.MyConfig")
4229 .target_name("my_config")
4230 .config_type(cm_rust::ConfigValueType::Int8)
4231 .build();
4232 let components = vec![
4233 (
4234 "a",
4235 ComponentDeclBuilder::new()
4236 .capability(
4237 CapabilityBuilder::config()
4238 .name("fuchsia.MyConfig")
4239 .value(good_value.clone().into()),
4240 )
4241 .offer(
4242 OfferBuilder::config()
4243 .name("fuchsia.MyConfig")
4244 .source(cm_rust::OfferSource::Self_)
4245 .target(cm_rust::OfferTarget::Child(cm_rust::ChildRef {
4246 name: "b".parse().unwrap(),
4247 collection: None,
4248 })),
4249 )
4250 .child_default("b")
4251 .build(),
4252 ),
4253 (
4254 "b",
4255 ComponentDeclBuilder::new()
4256 .use_(use_config.clone())
4257 .config(cm_rust::ConfigDecl {
4258 fields: vec![cm_rust::ConfigField {
4259 key: "my_config".into(),
4260 type_: cm_rust::ConfigValueType::Int8,
4261 mutability: Default::default(),
4262 }],
4263 checksum: cm_rust::ConfigChecksum::Sha256([0; 32]),
4264 value_source: cm_rust::ConfigValueSource::Capabilities(Default::default()),
4265 })
4266 .build(),
4267 ),
4268 ];
4269
4270 let model = T::new("a", components).build().await;
4271 let child_component = model.look_up_instance(&vec!["b"].try_into().unwrap()).await.unwrap();
4272
4273 let cm_rust::UseDecl::Config(use_config) = use_config else { panic!() };
4274 let value =
4275 routing::config::route_config_value(&use_config, &child_component).await.unwrap();
4276 assert_eq!(value, Some(cm_rust::ConfigValue::Single(good_value)));
4277 }
4278
4279 pub async fn test_use_config_from_void(&self) {
4286 let mut use_config = UseBuilder::config()
4287 .source(cm_rust::UseSource::Parent)
4288 .name("fuchsia.MyConfig")
4289 .target_name("my_config")
4290 .availability(cm_rust::Availability::Optional)
4291 .config_type(cm_rust::ConfigValueType::Int8)
4292 .build();
4293 match &mut use_config {
4294 cm_rust::UseDecl::Config(use_config_decl) => {
4295 use_config_decl.default =
4296 Some(cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Int8(0)))
4297 }
4298 _ => panic!("unexpected use declaration variant"),
4299 }
4300 let components = vec![
4301 (
4302 "a",
4303 ComponentDeclBuilder::new()
4304 .offer(
4305 OfferBuilder::config()
4306 .name("fuchsia.MyConfig")
4307 .source(cm_rust::OfferSource::Void)
4308 .availability(cm_rust::Availability::Optional)
4309 .target(cm_rust::OfferTarget::Child(cm_rust::ChildRef {
4310 name: "b".parse().unwrap(),
4311 collection: None,
4312 })),
4313 )
4314 .child_default("b")
4315 .build(),
4316 ),
4317 (
4318 "b",
4319 ComponentDeclBuilder::new()
4320 .use_(use_config.clone())
4321 .config(cm_rust::ConfigDecl {
4322 fields: vec![cm_rust::ConfigField {
4323 key: "my_config".into(),
4324 type_: cm_rust::ConfigValueType::Int8,
4325 mutability: Default::default(),
4326 }],
4327 checksum: cm_rust::ConfigChecksum::Sha256([0; 32]),
4328 value_source: cm_rust::ConfigValueSource::Capabilities(Default::default()),
4329 })
4330 .build(),
4331 ),
4332 ];
4333
4334 let model = T::new("a", components).build().await;
4335 let child_component = model.look_up_instance(&vec!["b"].try_into().unwrap()).await.unwrap();
4336
4337 let cm_rust::UseDecl::Config(use_config) = use_config else { panic!() };
4338 let value =
4339 routing::config::route_config_value(&use_config, &child_component).await.unwrap();
4340 assert_eq!(value, Some(cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Int8(0))));
4341 }
4342
4343 pub async fn test_use_dictionary_protocol_from_self(&self) {
4344 let components = vec![(
4345 "root",
4346 ComponentDeclBuilder::new()
4347 .dictionary_default("my_dict")
4348 .use_(
4349 UseBuilder::protocol()
4350 .source(UseSource::Self_)
4351 .name("A")
4352 .path("/svc/B")
4353 .from_dictionary("my_dict"),
4354 )
4355 .build(),
4356 )];
4357
4358 let model = T::new("root", components).build().await;
4359 let root_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
4360
4361 let route_result = route_capability(
4362 RouteRequest::UseProtocol(UseProtocolDecl {
4363 source: UseSource::Self_,
4364 source_name: "A".parse().unwrap(),
4365 source_dictionary: "my_dict".parse().unwrap(),
4366 target_path: "/svc/B".parse().unwrap(),
4367 dependency_type: DependencyType::Strong,
4368 availability: Availability::Required,
4369 }),
4370 &root_component,
4371 &mut NoopRouteMapper,
4372 )
4373 .await;
4374
4375 assert_matches!(
4376 route_result,
4377 Err(RoutingError::UseFromSelfNotFound { moniker, capability_id })
4378 if capability_id == "my_dict/A" && moniker == Moniker::root()
4379 );
4380 }
4381
4382 pub async fn test_offer_dictionary_to_grandchild_not_supported(&self) {
4383 let components = vec![
4388 (
4389 "root",
4390 ComponentDeclBuilder::new()
4391 .dictionary_default("parent_dict")
4395 .offer(
4396 OfferBuilder::protocol()
4397 .name("A")
4398 .from_dictionary("parent_dict")
4399 .source(OfferSource::Self_)
4400 .target_static_child("intermediate"),
4401 )
4402 .child_default("intermediate")
4403 .build(),
4404 ),
4405 (
4406 "intermediate",
4407 ComponentDeclBuilder::new()
4408 .offer(
4409 OfferBuilder::protocol()
4410 .source(OfferSource::Parent)
4411 .name("A")
4412 .target_static_child("leaf"),
4413 )
4414 .child_default("leaf")
4415 .build(),
4416 ),
4417 (
4418 "leaf",
4419 ComponentDeclBuilder::new()
4420 .use_(UseBuilder::protocol().source(UseSource::Parent).name("A"))
4421 .build(),
4422 ),
4423 ];
4424
4425 let model = T::new("root", components).build().await;
4426 let leaf_component = model
4427 .look_up_instance(&vec!["intermediate", "leaf"].try_into().unwrap())
4428 .await
4429 .expect("leaf instance");
4430
4431 let route_result = route_capability(
4432 RouteRequest::UseProtocol(UseProtocolDecl {
4433 source: UseSource::Parent,
4434 source_name: "A".parse().unwrap(),
4435 source_dictionary: Default::default(),
4436 target_path: "/svc/dict_protocol".parse().unwrap(),
4437 dependency_type: DependencyType::Strong,
4438 availability: Availability::Required,
4439 }),
4440 &leaf_component,
4441 &mut NoopRouteMapper,
4442 )
4443 .await;
4444
4445 assert_matches!(
4446 route_result,
4447 Err(RoutingError::BedrockNotPresentInDictionary { name, .. })
4448 if name == "svc/dict_protocol"
4449 );
4450 }
4451
4452 pub async fn test_expose_dictionary_to_grandparent_not_supported(&self) {
4453 let components = vec![
4456 (
4457 "root",
4458 ComponentDeclBuilder::new()
4459 .use_(
4460 UseBuilder::protocol()
4461 .source(UseSource::Child("intermediate".parse().unwrap()))
4462 .name("A"),
4463 )
4464 .child_default("intermediate")
4465 .build(),
4466 ),
4467 (
4468 "intermediate",
4469 ComponentDeclBuilder::new()
4470 .expose(
4471 ExposeBuilder::protocol()
4472 .source(ExposeSource::Child("leaf".parse().unwrap()))
4473 .name("A")
4474 .target(ExposeTarget::Parent),
4475 )
4476 .child_default("leaf")
4477 .build(),
4478 ),
4479 (
4480 "leaf",
4481 ComponentDeclBuilder::new()
4482 .dictionary_default("child_dict")
4483 .expose(
4484 ExposeBuilder::protocol()
4485 .name("A")
4486 .from_dictionary("child_dict")
4487 .source(ExposeSource::Self_)
4488 .target(ExposeTarget::Parent),
4489 )
4490 .build(),
4491 ),
4492 ];
4493
4494 let model = T::new("root", components).build().await;
4495 let root_component = model.look_up_instance(&Moniker::root()).await.expect("root instance");
4496 let route_result = route_capability(
4497 RouteRequest::UseProtocol(UseProtocolDecl {
4498 source: UseSource::Child("intermediate".parse().unwrap()),
4499 source_name: "A".parse().unwrap(),
4500 source_dictionary: Default::default(),
4501 target_path: "/svc/dict_protocol".parse().unwrap(),
4502 dependency_type: DependencyType::Strong,
4503 availability: Availability::Required,
4504 }),
4505 &root_component,
4506 &mut NoopRouteMapper,
4507 )
4508 .await;
4509
4510 assert_matches!(
4511 route_result,
4512 Err(RoutingError::BedrockNotPresentInDictionary { name, .. })
4513 if name == "svc/dict_protocol"
4514 );
4515 }
4516}