1use crate::error::Error;
6use crate::local_component_runner::LocalComponentRunnerBuilder;
7use anyhow::{format_err, Context as _};
8use cm_rust::{FidlIntoNative, NativeIntoFidl};
9use component_events::events::Started;
10use component_events::matcher::EventMatcher;
11use fidl::endpoints::{
12 self, create_proxy, ClientEnd, DiscoverableProtocolMarker, Proxy, ServerEnd, ServiceMarker,
13};
14use fuchsia_component::client as fclient;
15use futures::future::BoxFuture;
16use futures::{FutureExt, TryFutureExt, TryStreamExt};
17use log::*;
18use std::collections::HashMap;
19use std::fmt::{self, Display, Formatter};
20use {
21 fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_decl as fdecl,
22 fidl_fuchsia_component_test as ftest, fidl_fuchsia_io as fio, fidl_fuchsia_mem as fmem,
23 fidl_fuchsia_sys2 as fsys, fuchsia_async as fasync,
24};
25
26pub mod new {
27 pub use super::*;
28}
29
30pub const DEFAULT_COLLECTION_NAME: &'static str = "realm_builder";
32
33const REALM_BUILDER_SERVER_CHILD_NAME: &'static str = "realm_builder_server";
34
35pub mod error;
36mod local_component_runner;
37
38pub use local_component_runner::LocalComponentHandles;
39
40#[derive(Debug, Clone, PartialEq, Eq, Hash)]
42pub struct Ref {
43 value: RefInner,
44
45 scope: Option<Vec<String>>,
48}
49
50#[derive(Debug, Clone, PartialEq, Eq, Hash)]
51enum RefInner {
52 Capability(String),
53 Child(String),
54 Collection(String),
55 Debug,
56 Framework,
57 Parent,
58 Self_,
59 Void,
60}
61
62impl Ref {
63 pub fn capability(name: impl Into<String>) -> Ref {
64 Ref { value: RefInner::Capability(name.into()), scope: None }
65 }
66
67 pub fn dictionary(path: impl Into<String>) -> Ref {
70 let path: String = path.into();
71 let parts: Vec<_> = path.split('/').collect();
72 if parts.len() != 2 || parts[0] != "self" {
73 panic!(
74 "Input to dictionary() must have the form \"self/<dictionary_name>\", \
75 was: {path}"
76 );
77 }
78 Self::capability(parts[1])
79 }
80
81 pub fn child(name: impl Into<String>) -> Ref {
82 Ref { value: RefInner::Child(name.into()), scope: None }
83 }
84
85 pub fn collection(name: impl Into<String>) -> Ref {
86 Ref { value: RefInner::Collection(name.into()), scope: None }
87 }
88
89 pub fn debug() -> Ref {
90 Ref { value: RefInner::Debug, scope: None }
91 }
92
93 pub fn framework() -> Ref {
94 Ref { value: RefInner::Framework, scope: None }
95 }
96
97 pub fn parent() -> Ref {
98 Ref { value: RefInner::Parent, scope: None }
99 }
100
101 pub fn self_() -> Ref {
102 Ref { value: RefInner::Self_, scope: None }
103 }
104
105 pub fn void() -> Ref {
106 Ref { value: RefInner::Void, scope: None }
107 }
108
109 #[allow(clippy::result_large_err)] fn check_scope(&self, realm_scope: &Vec<String>) -> Result<(), Error> {
111 if let Some(ref_scope) = self.scope.as_ref() {
112 if ref_scope != realm_scope {
113 return Err(Error::RefUsedInWrongRealm(self.clone(), realm_scope.join("/")));
114 }
115 }
116 Ok(())
117 }
118}
119
120impl Into<fdecl::Ref> for Ref {
121 fn into(self) -> fdecl::Ref {
122 match self.value {
123 RefInner::Capability(name) => fdecl::Ref::Capability(fdecl::CapabilityRef { name }),
124 RefInner::Child(name) => fdecl::Ref::Child(fdecl::ChildRef { name, collection: None }),
125 RefInner::Collection(name) => fdecl::Ref::Collection(fdecl::CollectionRef { name }),
126 RefInner::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
127 RefInner::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
128 RefInner::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
129 RefInner::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
130 RefInner::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
131 }
132 }
133}
134
135impl From<&SubRealmBuilder> for Ref {
138 fn from(input: &SubRealmBuilder) -> Ref {
139 let mut scope = input.realm_path.clone();
142 let child_name = scope.pop().expect("this should be impossible");
143 Ref { value: RefInner::Child(child_name), scope: Some(scope) }
144 }
145}
146
147impl From<&ChildRef> for Ref {
148 fn from(input: &ChildRef) -> Ref {
149 Ref { value: RefInner::Child(input.name.clone()), scope: input.scope.clone() }
150 }
151}
152
153impl From<&CollectionRef> for Ref {
154 fn from(input: &CollectionRef) -> Ref {
155 Ref { value: RefInner::Collection(input.name.clone()), scope: input.scope.clone() }
156 }
157}
158
159impl Display for Ref {
160 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
161 match &self.value {
162 RefInner::Capability(name) => {
163 write!(f, "capability {}", name)?;
164 }
165 RefInner::Child(name) => {
166 write!(f, "child {}", name)?;
167 }
168 RefInner::Collection(name) => {
169 write!(f, "collection {}", name)?;
170 }
171 RefInner::Debug => {
172 write!(f, "debug")?;
173 }
174 RefInner::Framework => {
175 write!(f, "framework")?;
176 }
177 RefInner::Parent => {
178 write!(f, "parent")?;
179 }
180 RefInner::Self_ => {
181 write!(f, "self")?;
182 }
183 RefInner::Void => {
184 write!(f, "void")?;
185 }
186 }
187 if let Some(ref_scope) = self.scope.as_ref() {
188 write!(f, " in realm {:?}", ref_scope.join("/"))?;
189 }
190 Ok(())
191 }
192}
193
194#[derive(Debug, Clone, PartialEq)]
198pub struct ChildRef {
199 name: String,
200 scope: Option<Vec<String>>,
201}
202
203impl ChildRef {
204 fn new(name: String, scope: Vec<String>) -> Self {
205 ChildRef { name, scope: Some(scope) }
206 }
207
208 #[allow(clippy::result_large_err)] fn check_scope(&self, realm_scope: &Vec<String>) -> Result<(), Error> {
210 if let Some(ref_scope) = self.scope.as_ref() {
211 if ref_scope != realm_scope {
212 return Err(Error::RefUsedInWrongRealm(self.into(), realm_scope.join("/")));
213 }
214 }
215 Ok(())
216 }
217}
218
219impl From<String> for ChildRef {
220 fn from(input: String) -> ChildRef {
221 ChildRef { name: input, scope: None }
222 }
223}
224
225impl From<&str> for ChildRef {
226 fn from(input: &str) -> ChildRef {
227 ChildRef { name: input.to_string(), scope: None }
228 }
229}
230
231impl From<&SubRealmBuilder> for ChildRef {
232 fn from(input: &SubRealmBuilder) -> ChildRef {
233 let mut scope = input.realm_path.clone();
236 let child_name = scope.pop().expect("this should be impossible");
237 ChildRef { name: child_name, scope: Some(scope) }
238 }
239}
240
241impl From<&ChildRef> for ChildRef {
242 fn from(input: &ChildRef) -> ChildRef {
243 input.clone()
244 }
245}
246
247#[derive(Debug, Clone, PartialEq)]
251pub struct CollectionRef {
252 name: String,
253 scope: Option<Vec<String>>,
254}
255
256impl CollectionRef {
257 fn new(name: String, scope: Vec<String>) -> Self {
258 CollectionRef { name, scope: Some(scope) }
259 }
260}
261
262impl From<String> for CollectionRef {
263 fn from(input: String) -> CollectionRef {
264 CollectionRef { name: input, scope: None }
265 }
266}
267
268impl From<&str> for CollectionRef {
269 fn from(input: &str) -> CollectionRef {
270 CollectionRef { name: input.to_string(), scope: None }
271 }
272}
273
274impl From<&SubRealmBuilder> for CollectionRef {
275 fn from(input: &SubRealmBuilder) -> CollectionRef {
276 let mut scope = input.realm_path.clone();
279 let collection_name = scope.pop().expect("this should be impossible");
280 CollectionRef { name: collection_name, scope: Some(scope) }
281 }
282}
283
284impl From<&CollectionRef> for CollectionRef {
285 fn from(input: &CollectionRef) -> CollectionRef {
286 input.clone()
287 }
288}
289
290pub struct Capability;
292
293impl Capability {
294 pub fn protocol<P: DiscoverableProtocolMarker>() -> ProtocolCapability {
296 Self::protocol_by_name(P::PROTOCOL_NAME)
297 }
298
299 pub fn protocol_by_name(name: impl Into<String>) -> ProtocolCapability {
301 ProtocolCapability {
302 name: name.into(),
303 as_: None,
304 type_: fdecl::DependencyType::Strong,
305 path: None,
306 availability: None,
307 }
308 }
309
310 pub fn configuration(name: impl Into<String>) -> ConfigurationCapability {
312 ConfigurationCapability { name: name.into(), as_: None, availability: None }
313 }
314
315 pub fn directory(name: impl Into<String>) -> DirectoryCapability {
317 DirectoryCapability {
318 name: name.into(),
319 as_: None,
320 type_: fdecl::DependencyType::Strong,
321 rights: None,
322 subdir: None,
323 path: None,
324 availability: None,
325 }
326 }
327
328 pub fn storage(name: impl Into<String>) -> StorageCapability {
330 StorageCapability { name: name.into(), as_: None, path: None, availability: None }
331 }
332
333 pub fn service<S: ServiceMarker>() -> ServiceCapability {
335 Self::service_by_name(S::SERVICE_NAME)
336 }
337
338 pub fn service_by_name(name: impl Into<String>) -> ServiceCapability {
340 ServiceCapability { name: name.into(), as_: None, path: None, availability: None }
341 }
342
343 pub fn event_stream(name: impl Into<String>) -> EventStream {
345 EventStream { name: name.into(), rename: None, path: None, scope: None }
346 }
347
348 pub fn dictionary(name: impl Into<String>) -> DictionaryCapability {
350 DictionaryCapability { name: name.into(), as_: None, availability: None }
351 }
352
353 pub fn resolver(name: impl Into<String>) -> ResolverCapability {
355 ResolverCapability { name: name.into(), as_: None, path: None }
356 }
357
358 pub fn runner(name: impl Into<String>) -> RunnerCapability {
360 RunnerCapability { name: name.into(), as_: None, path: None }
361 }
362}
363
364#[derive(Debug, Clone, PartialEq)]
367pub struct ProtocolCapability {
368 name: String,
369 as_: Option<String>,
370 type_: fdecl::DependencyType,
371 path: Option<String>,
372 availability: Option<fdecl::Availability>,
373}
374
375impl ProtocolCapability {
376 pub fn as_(mut self, as_: impl Into<String>) -> Self {
378 self.as_ = Some(as_.into());
379 self
380 }
381
382 pub fn weak(mut self) -> Self {
385 self.type_ = fdecl::DependencyType::Weak;
386 self
387 }
388
389 pub fn path(mut self, path: impl Into<String>) -> Self {
393 self.path = Some(path.into());
394 self
395 }
396
397 pub fn optional(mut self) -> Self {
400 self.availability = Some(fdecl::Availability::Optional);
401 self
402 }
403
404 pub fn availability_same_as_target(mut self) -> Self {
407 self.availability = Some(fdecl::Availability::SameAsTarget);
408 self
409 }
410}
411
412impl Into<ftest::Capability> for ProtocolCapability {
413 fn into(self) -> ftest::Capability {
414 ftest::Capability::Protocol(ftest::Protocol {
415 name: Some(self.name),
416 as_: self.as_,
417 type_: Some(self.type_),
418 path: self.path,
419 availability: self.availability,
420 ..Default::default()
421 })
422 }
423}
424
425#[derive(Debug, Clone, PartialEq)]
428pub struct ConfigurationCapability {
429 name: String,
430 as_: Option<String>,
431 availability: Option<fdecl::Availability>,
432}
433
434impl ConfigurationCapability {
435 pub fn as_(mut self, name: impl Into<String>) -> Self {
437 self.as_ = Some(name.into());
438 self
439 }
440
441 pub fn optional(mut self) -> Self {
444 self.availability = Some(fdecl::Availability::Optional);
445 self
446 }
447
448 pub fn availability_same_as_target(mut self) -> Self {
451 self.availability = Some(fdecl::Availability::SameAsTarget);
452 self
453 }
454}
455
456impl Into<ftest::Capability> for ConfigurationCapability {
457 fn into(self) -> ftest::Capability {
458 ftest::Capability::Config(ftest::Config {
459 name: Some(self.name),
460 as_: self.as_,
461 availability: self.availability,
462 ..Default::default()
463 })
464 }
465}
466
467#[derive(Debug, Clone, PartialEq)]
470pub struct DirectoryCapability {
471 name: String,
472 as_: Option<String>,
473 type_: fdecl::DependencyType,
474 rights: Option<fio::Operations>,
475 subdir: Option<String>,
476 path: Option<String>,
477 availability: Option<fdecl::Availability>,
478}
479
480impl DirectoryCapability {
481 pub fn as_(mut self, as_: impl Into<String>) -> Self {
483 self.as_ = Some(as_.into());
484 self
485 }
486
487 pub fn weak(mut self) -> Self {
490 self.type_ = fdecl::DependencyType::Weak;
491 self
492 }
493
494 pub fn rights(mut self, rights: fio::Operations) -> Self {
496 self.rights = Some(rights);
497 self
498 }
499
500 pub fn subdir(mut self, subdir: impl Into<String>) -> Self {
502 self.subdir = Some(subdir.into());
503 self
504 }
505
506 pub fn path(mut self, path: impl Into<String>) -> Self {
509 self.path = Some(path.into());
510 self
511 }
512
513 pub fn optional(mut self) -> Self {
516 self.availability = Some(fdecl::Availability::Optional);
517 self
518 }
519
520 pub fn availability_same_as_target(mut self) -> Self {
523 self.availability = Some(fdecl::Availability::SameAsTarget);
524 self
525 }
526}
527
528impl Into<ftest::Capability> for DirectoryCapability {
529 fn into(self) -> ftest::Capability {
530 ftest::Capability::Directory(ftest::Directory {
531 name: Some(self.name),
532 as_: self.as_,
533 type_: Some(self.type_),
534 rights: self.rights,
535 subdir: self.subdir,
536 path: self.path,
537 availability: self.availability,
538 ..Default::default()
539 })
540 }
541}
542
543#[derive(Debug, Clone, PartialEq)]
546pub struct StorageCapability {
547 name: String,
548 as_: Option<String>,
549 path: Option<String>,
550 availability: Option<fdecl::Availability>,
551}
552
553impl StorageCapability {
554 pub fn as_(mut self, as_: impl Into<String>) -> Self {
556 self.as_ = Some(as_.into());
557 self
558 }
559
560 pub fn path(mut self, path: impl Into<String>) -> Self {
563 self.path = Some(path.into());
564 self
565 }
566
567 pub fn optional(mut self) -> Self {
570 self.availability = Some(fdecl::Availability::Optional);
571 self
572 }
573
574 pub fn availability_same_as_target(mut self) -> Self {
577 self.availability = Some(fdecl::Availability::SameAsTarget);
578 self
579 }
580}
581
582impl Into<ftest::Capability> for StorageCapability {
583 fn into(self) -> ftest::Capability {
584 ftest::Capability::Storage(ftest::Storage {
585 name: Some(self.name),
586 as_: self.as_,
587 path: self.path,
588 availability: self.availability,
589 ..Default::default()
590 })
591 }
592}
593
594#[derive(Debug, Clone, PartialEq)]
597pub struct ServiceCapability {
598 name: String,
599 as_: Option<String>,
600 path: Option<String>,
601 availability: Option<fdecl::Availability>,
602}
603
604impl ServiceCapability {
605 pub fn as_(mut self, as_: impl Into<String>) -> Self {
607 self.as_ = Some(as_.into());
608 self
609 }
610
611 pub fn path(mut self, path: impl Into<String>) -> Self {
615 self.path = Some(path.into());
616 self
617 }
618
619 pub fn optional(mut self) -> Self {
622 self.availability = Some(fdecl::Availability::Optional);
623 self
624 }
625
626 pub fn availability_same_as_target(mut self) -> Self {
629 self.availability = Some(fdecl::Availability::SameAsTarget);
630 self
631 }
632}
633
634impl Into<ftest::Capability> for ServiceCapability {
635 fn into(self) -> ftest::Capability {
636 ftest::Capability::Service(ftest::Service {
637 name: Some(self.name),
638 as_: self.as_,
639 path: self.path,
640 availability: self.availability,
641 ..Default::default()
642 })
643 }
644}
645
646impl Into<ftest::Capability> for EventStream {
647 fn into(self) -> ftest::Capability {
648 ftest::Capability::EventStream(ftest::EventStream {
649 name: Some(self.name),
650 as_: self.rename,
651 scope: self.scope.map(|scopes| scopes.into_iter().map(|scope| scope.into()).collect()),
652 path: self.path,
653 ..Default::default()
654 })
655 }
656}
657
658#[derive(Debug, Clone, PartialEq)]
661pub struct DictionaryCapability {
662 name: String,
663 as_: Option<String>,
664 availability: Option<fdecl::Availability>,
665}
666
667impl DictionaryCapability {
668 pub fn as_(mut self, as_: impl Into<String>) -> Self {
670 self.as_ = Some(as_.into());
671 self
672 }
673
674 pub fn optional(mut self) -> Self {
677 self.availability = Some(fdecl::Availability::Optional);
678 self
679 }
680
681 pub fn availability_same_as_target(mut self) -> Self {
684 self.availability = Some(fdecl::Availability::SameAsTarget);
685 self
686 }
687}
688
689#[cfg(fuchsia_api_level_at_least = "25")]
690impl Into<ftest::Capability> for DictionaryCapability {
691 fn into(self) -> ftest::Capability {
692 ftest::Capability::Dictionary(ftest::Dictionary {
693 name: Some(self.name),
694 as_: self.as_,
695 availability: self.availability,
696 ..Default::default()
697 })
698 }
699}
700
701#[derive(Debug, Clone, PartialEq)]
704pub struct ResolverCapability {
705 name: String,
706 as_: Option<String>,
707 path: Option<String>,
708}
709
710impl ResolverCapability {
711 pub fn as_(mut self, as_: impl Into<String>) -> Self {
713 self.as_ = Some(as_.into());
714 self
715 }
716
717 pub fn path(mut self, path: impl Into<String>) -> Self {
721 self.path = Some(path.into());
722 self
723 }
724}
725
726#[cfg(fuchsia_api_level_at_least = "24")]
727impl Into<ftest::Capability> for ResolverCapability {
728 fn into(self) -> ftest::Capability {
729 ftest::Capability::Resolver(ftest::Resolver {
730 name: Some(self.name),
731 as_: self.as_,
732 path: self.path,
733 ..Default::default()
734 })
735 }
736}
737
738#[derive(Debug, Clone, PartialEq)]
741pub struct RunnerCapability {
742 name: String,
743 as_: Option<String>,
744 path: Option<String>,
745}
746
747impl RunnerCapability {
748 pub fn as_(mut self, as_: impl Into<String>) -> Self {
750 self.as_ = Some(as_.into());
751 self
752 }
753
754 pub fn path(mut self, path: impl Into<String>) -> Self {
758 self.path = Some(path.into());
759 self
760 }
761}
762
763#[cfg(fuchsia_api_level_at_least = "24")]
764impl Into<ftest::Capability> for RunnerCapability {
765 fn into(self) -> ftest::Capability {
766 ftest::Capability::Runner(ftest::Runner {
767 name: Some(self.name),
768 as_: self.as_,
769 path: self.path,
770 ..Default::default()
771 })
772 }
773}
774
775#[derive(Debug, Clone, PartialEq)]
777pub struct Route {
778 capabilities: Vec<ftest::Capability>,
779 from: Option<Ref>,
780 from_dictionary: Option<String>,
781 to: Vec<Ref>,
782}
783
784impl Route {
785 pub fn new() -> Self {
786 Self { capabilities: vec![], from: None, from_dictionary: None, to: vec![] }
787 }
788
789 pub fn capability(mut self, capability: impl Into<ftest::Capability>) -> Self {
791 self.capabilities.push(capability.into());
792 self
793 }
794
795 pub fn from(mut self, from: impl Into<Ref>) -> Self {
798 if self.from.is_some() {
799 panic!("from is already set for this route");
800 }
801 self.from = Some(from.into());
802 self
803 }
804
805 pub fn from_dictionary(mut self, from_dictionary: impl Into<String>) -> Self {
821 if self.from_dictionary.is_some() {
822 panic!("from_dictionary is already set for this route");
823 }
824 self.from_dictionary = Some(from_dictionary.into());
825 self
826 }
827
828 pub fn to(mut self, to: impl Into<Ref>) -> Self {
830 self.to.push(to.into());
831 self
832 }
833}
834
835pub struct RealmInstance {
838 pub root: ScopedInstance,
841
842 local_component_runner_task: Option<fasync::Task<()>>,
846}
847
848impl Drop for RealmInstance {
849 fn drop(&mut self) {
856 if !self.root.destroy_waiter_taken() {
857 let destroy_waiter = self.root.take_destroy_waiter();
858 let local_component_runner_task = self.local_component_runner_task.take();
859 fasync::Task::spawn(async move {
860 let _local_component_runner_task = local_component_runner_task;
862 let _ = destroy_waiter.await;
865 })
866 .detach();
867 }
868 debug!("RealmInstance is now shut down - the realm will be destroyed.");
871 }
872}
873
874impl RealmInstance {
875 pub async fn destroy(mut self) -> Result<(), Error> {
881 if self.root.destroy_waiter_taken() {
882 return Err(Error::DestroyWaiterTaken);
883 }
884 let _local_component_runner_task = self.local_component_runner_task.take();
885 let destroy_waiter = self.root.take_destroy_waiter();
886 drop(self);
887 destroy_waiter.await.map_err(Error::FailedToDestroyChild)?;
888 Ok(())
889 }
890
891 pub async fn start_component_tree(&self) -> Result<(), Error> {
895 let lifecycle_controller: fsys::LifecycleControllerProxy = self
896 .root
897 .connect_to_protocol_at_exposed_dir()
898 .map_err(|e| Error::CannotStartRootComponent(e))?;
899 let (_, binder_server) = fidl::endpoints::create_endpoints::<fcomponent::BinderMarker>();
900 lifecycle_controller.start_instance("./", binder_server).await?.map_err(|e| {
901 Error::CannotStartRootComponent(format_err!("received error status: {:?}", e))
902 })?;
903 Ok(())
904 }
905}
906
907#[derive(Debug, Clone)]
908pub struct RealmBuilderParams {
909 component_realm_proxy: Option<fcomponent::RealmProxy>,
910 collection_name: Option<String>,
911 fragment_only_url: Option<String>,
912 pkg_dir_proxy: Option<fio::DirectoryProxy>,
913 start: bool,
914 realm_name: Option<String>,
915}
916
917impl RealmBuilderParams {
918 pub fn new() -> Self {
919 Self {
920 component_realm_proxy: None,
921 collection_name: None,
922 fragment_only_url: None,
923 pkg_dir_proxy: None,
924 start: true,
925 realm_name: None,
926 }
927 }
928
929 pub fn with_realm_proxy(mut self, realm_proxy: fcomponent::RealmProxy) -> Self {
930 self.component_realm_proxy = Some(realm_proxy);
931 self
932 }
933
934 pub fn in_collection(mut self, collection_name: impl Into<String>) -> Self {
935 self.collection_name = Some(collection_name.into());
936 self
937 }
938
939 pub fn from_relative_url(mut self, fragment_only_url: impl Into<String>) -> Self {
940 self.fragment_only_url = Some(fragment_only_url.into());
941 self
942 }
943
944 pub fn with_pkg_dir_proxy(mut self, pkg_dir_proxy: fio::DirectoryProxy) -> Self {
945 self.pkg_dir_proxy = Some(pkg_dir_proxy);
946 self
947 }
948
949 pub fn start(mut self, start_on_build: bool) -> Self {
950 self.start = start_on_build;
951 self
952 }
953
954 pub fn realm_name(mut self, realm_name: impl Into<String>) -> Self {
955 self.realm_name = Some(realm_name.into());
956 self
957 }
958}
959
960#[derive(Debug)]
964pub struct RealmBuilder {
965 root_realm: SubRealmBuilder,
966 builder_proxy: ftest::BuilderProxy,
967 component_realm_proxy: fcomponent::RealmProxy,
968 local_component_runner_builder: LocalComponentRunnerBuilder,
969 collection_name: String,
970 start: bool,
971 realm_name: String,
972}
973
974impl RealmBuilder {
975 pub async fn new() -> Result<Self, Error> {
977 Self::with_params(RealmBuilderParams::new()).await
978 }
979
980 pub async fn with_params(params: RealmBuilderParams) -> Result<Self, Error> {
981 let component_realm_proxy = match params.component_realm_proxy {
982 Some(r) => r,
983 None => fclient::connect_to_protocol::<fcomponent::RealmMarker>()
984 .map_err(Error::ConnectToServer)?,
985 };
986 let pkg_dir_proxy = match params.pkg_dir_proxy {
987 Some(p) => p,
988 None => fuchsia_fs::directory::open_in_namespace(
989 "/pkg",
990 fuchsia_fs::PERM_READABLE | fuchsia_fs::PERM_EXECUTABLE,
991 )
992 .map_err(Error::FailedToOpenPkgDir)?,
993 };
994 let collection_name =
995 params.collection_name.unwrap_or_else(|| DEFAULT_COLLECTION_NAME.into());
996 let realm_name = params.realm_name.unwrap_or_else(|| {
997 let id: u64 = rand::random();
998 format!("auto-{:x}", id)
999 });
1000 Self::create(
1001 component_realm_proxy,
1002 collection_name,
1003 params.fragment_only_url,
1004 pkg_dir_proxy,
1005 params.start,
1006 realm_name,
1007 )
1008 .await
1009 }
1010
1011 async fn create(
1012 component_realm_proxy: fcomponent::RealmProxy,
1013 collection_name: String,
1014 fragment_only_url: Option<String>,
1015 pkg_dir_proxy: fio::DirectoryProxy,
1016 start: bool,
1017 realm_name: String,
1018 ) -> Result<Self, Error> {
1019 let (exposed_dir_proxy, exposed_dir_server_end) =
1020 endpoints::create_proxy::<fio::DirectoryMarker>();
1021 component_realm_proxy
1022 .open_exposed_dir(
1023 &fdecl::ChildRef {
1024 name: REALM_BUILDER_SERVER_CHILD_NAME.to_string(),
1025 collection: None,
1026 },
1027 exposed_dir_server_end,
1028 )
1029 .await?
1030 .map_err(|e| {
1031 Error::ConnectToServer(format_err!("failed to open exposed dir: {:?}", e))
1032 })?;
1033 let realm_builder_factory_proxy = fclient::connect_to_protocol_at_dir_root::<
1034 ftest::RealmBuilderFactoryMarker,
1035 >(&exposed_dir_proxy)
1036 .map_err(Error::ConnectToServer)?;
1037
1038 let (realm_proxy, realm_server_end) = create_proxy::<ftest::RealmMarker>();
1039 let (builder_proxy, builder_server_end) = create_proxy::<ftest::BuilderMarker>();
1040 match fragment_only_url {
1041 Some(fragment_only_url) => {
1042 realm_builder_factory_proxy
1043 .create_from_relative_url(
1044 ClientEnd::from(pkg_dir_proxy.into_channel().unwrap().into_zx_channel()),
1045 &fragment_only_url,
1046 realm_server_end,
1047 builder_server_end,
1048 )
1049 .await??;
1050 }
1051 None => {
1052 realm_builder_factory_proxy
1053 .create(
1054 ClientEnd::from(pkg_dir_proxy.into_channel().unwrap().into_zx_channel()),
1055 realm_server_end,
1056 builder_server_end,
1057 )
1058 .await??;
1059 }
1060 }
1061 Self::build_struct(
1062 component_realm_proxy,
1063 realm_proxy,
1064 builder_proxy,
1065 collection_name,
1066 start,
1067 realm_name,
1068 )
1069 }
1070
1071 #[allow(clippy::result_large_err)] fn build_struct(
1073 component_realm_proxy: fcomponent::RealmProxy,
1074 realm_proxy: ftest::RealmProxy,
1075 builder_proxy: ftest::BuilderProxy,
1076 collection_name: String,
1077 start: bool,
1078 realm_name: String,
1079 ) -> Result<Self, Error> {
1080 let local_component_runner_builder = LocalComponentRunnerBuilder::new();
1081 Ok(Self {
1082 root_realm: SubRealmBuilder {
1083 realm_proxy,
1084 realm_path: vec![],
1085 local_component_runner_builder: local_component_runner_builder.clone(),
1086 },
1087 component_realm_proxy,
1088 builder_proxy,
1089 local_component_runner_builder,
1090 collection_name,
1091 start,
1092 realm_name,
1093 })
1094 }
1095
1096 pub async fn initialize(self) -> Result<(String, fasync::Task<()>), Error> {
1101 let (component_runner_client_end, local_component_runner_task) =
1102 self.local_component_runner_builder.build().await?;
1103 let root_url = self.builder_proxy.build(component_runner_client_end).await??;
1104 Ok((root_url, local_component_runner_task))
1105 }
1106
1107 pub async fn build(self) -> Result<RealmInstance, Error> {
1114 let (component_runner_client_end, local_component_runner_task) =
1115 self.local_component_runner_builder.build().await?;
1116 let root_url = self.builder_proxy.build(component_runner_client_end).await??;
1117 let factory = ScopedInstanceFactory::new(self.collection_name)
1118 .with_realm_proxy(self.component_realm_proxy);
1119 let root = factory
1120 .new_named_instance(self.realm_name, root_url)
1121 .await
1122 .map_err(Error::FailedToCreateChild)?;
1123 let realm =
1124 RealmInstance { root, local_component_runner_task: Some(local_component_runner_task) };
1125 if self.start {
1126 realm.root.connect_to_binder().map_err(Error::FailedToBind)?;
1127 }
1128 Ok(realm)
1129 }
1130
1131 pub async fn with_nested_component_manager(
1142 self,
1143 component_manager_fragment_only_url: &str,
1144 ) -> Result<Self, Error> {
1145 self.root_realm.with_nested_component_manager(component_manager_fragment_only_url).await?;
1146 Ok(self)
1147 }
1148
1149 pub async fn build_in_nested_component_manager(
1158 self,
1159 component_manager_fragment_only_url: &str,
1160 ) -> Result<RealmInstance, Error> {
1161 let component_manager_realm =
1162 self.with_nested_component_manager(component_manager_fragment_only_url).await?;
1163 let cm_instance = component_manager_realm.build().await?;
1164 Ok(cm_instance)
1165 }
1166
1167 pub async fn add_child_realm(
1174 &self,
1175 name: impl Into<String>,
1176 options: ChildOptions,
1177 ) -> Result<SubRealmBuilder, Error> {
1178 self.root_realm.add_child_realm(name, options).await
1179 }
1180
1181 pub async fn add_child_realm_from_relative_url(
1182 &self,
1183 name: impl Into<String>,
1184 relative_url: impl Into<String>,
1185 options: ChildOptions,
1186 ) -> Result<SubRealmBuilder, Error> {
1187 self.root_realm.add_child_realm_from_relative_url(name, relative_url, options).await
1188 }
1189
1190 #[cfg(fuchsia_api_level_at_least = "26")]
1191 pub async fn add_child_realm_from_decl(
1192 &self,
1193 name: impl Into<String>,
1194 decl: cm_rust::ComponentDecl,
1195 options: ChildOptions,
1196 ) -> Result<SubRealmBuilder, Error> {
1197 self.root_realm.add_child_realm_from_decl(name, decl, options).await
1198 }
1199
1200 pub async fn add_local_child(
1202 &self,
1203 name: impl Into<String>,
1204 local_component_implementation: impl Fn(LocalComponentHandles) -> BoxFuture<'static, Result<(), anyhow::Error>>
1205 + Sync
1206 + Send
1207 + 'static,
1208 options: ChildOptions,
1209 ) -> Result<ChildRef, Error> {
1210 self.root_realm.add_local_child(name, local_component_implementation, options).await
1211 }
1212
1213 pub async fn add_child(
1215 &self,
1216 name: impl Into<String>,
1217 url: impl Into<String>,
1218 options: ChildOptions,
1219 ) -> Result<ChildRef, Error> {
1220 self.root_realm.add_child(name, url, options).await
1221 }
1222
1223 pub async fn add_child_from_decl(
1225 &self,
1226 name: impl Into<String>,
1227 decl: cm_rust::ComponentDecl,
1228 options: ChildOptions,
1229 ) -> Result<ChildRef, Error> {
1230 self.root_realm.add_child_from_decl(name, decl, options).await
1231 }
1232
1233 pub async fn get_component_decl(
1244 &self,
1245 name: impl Into<ChildRef>,
1246 ) -> Result<cm_rust::ComponentDecl, Error> {
1247 self.root_realm.get_component_decl(name).await
1248 }
1249
1250 pub async fn replace_component_decl(
1261 &self,
1262 name: impl Into<ChildRef>,
1263 decl: cm_rust::ComponentDecl,
1264 ) -> Result<(), Error> {
1265 self.root_realm.replace_component_decl(name, decl).await
1266 }
1267
1268 pub async fn get_realm_decl(&self) -> Result<cm_rust::ComponentDecl, Error> {
1270 self.root_realm.get_realm_decl().await
1271 }
1272
1273 pub async fn replace_realm_decl(&self, decl: cm_rust::ComponentDecl) -> Result<(), Error> {
1275 self.root_realm.replace_realm_decl(decl).await
1276 }
1277
1278 pub async fn add_route(&self, route: Route) -> Result<(), Error> {
1280 self.root_realm.add_route(route).await
1281 }
1282
1283 pub async fn init_mutable_config_from_package(
1285 &self,
1286 name: impl Into<ChildRef>,
1287 ) -> Result<(), Error> {
1288 self.root_realm.init_mutable_config_from_package(name).await
1289 }
1290
1291 pub async fn init_mutable_config_to_empty(
1293 &self,
1294 name: impl Into<ChildRef>,
1295 ) -> Result<(), Error> {
1296 self.root_realm.init_mutable_config_to_empty(name).await
1297 }
1298
1299 pub async fn set_config_value(
1301 &self,
1302 name: impl Into<ChildRef>,
1303 key: &str,
1304 value: cm_rust::ConfigValue,
1305 ) -> Result<(), Error> {
1306 self.root_realm.set_config_value(name, key, value).await
1307 }
1308
1309 pub async fn read_only_directory(
1313 &self,
1314 directory_name: impl Into<String>,
1315 to: Vec<impl Into<Ref>>,
1316 directory_contents: DirectoryContents,
1317 ) -> Result<(), Error> {
1318 self.root_realm.read_only_directory(directory_name, to, directory_contents).await
1319 }
1320
1321 pub async fn add_capability(&self, capability: cm_rust::CapabilityDecl) -> Result<(), Error> {
1323 self.root_realm.add_capability(capability).await
1324 }
1325
1326 #[cfg(fuchsia_api_level_at_least = "25")]
1327 pub async fn add_collection(
1329 &self,
1330 collection: cm_rust::CollectionDecl,
1331 ) -> Result<CollectionRef, Error> {
1332 self.root_realm.add_collection(collection).await
1333 }
1334
1335 #[cfg(fuchsia_api_level_at_least = "25")]
1336 pub async fn add_environment(
1338 &self,
1339 environment: cm_rust::EnvironmentDecl,
1340 ) -> Result<(), Error> {
1341 self.root_realm.add_environment(environment).await
1342 }
1343}
1344
1345#[derive(Debug, Clone)]
1346pub struct SubRealmBuilder {
1347 realm_proxy: ftest::RealmProxy,
1348 realm_path: Vec<String>,
1349 local_component_runner_builder: LocalComponentRunnerBuilder,
1350}
1351
1352impl SubRealmBuilder {
1353 pub async fn add_child_realm(
1354 &self,
1355 name: impl Into<String>,
1356 options: ChildOptions,
1357 ) -> Result<Self, Error> {
1358 let name: String = name.into();
1359 let (child_realm_proxy, child_realm_server_end) = create_proxy::<ftest::RealmMarker>();
1360 self.realm_proxy.add_child_realm(&name, &options.into(), child_realm_server_end).await??;
1361
1362 let mut child_path = self.realm_path.clone();
1363 child_path.push(name);
1364 Ok(SubRealmBuilder {
1365 realm_proxy: child_realm_proxy,
1366 realm_path: child_path,
1367 local_component_runner_builder: self.local_component_runner_builder.clone(),
1368 })
1369 }
1370
1371 pub async fn add_child_realm_from_relative_url(
1372 &self,
1373 name: impl Into<String>,
1374 relative_url: impl Into<String>,
1375 options: ChildOptions,
1376 ) -> Result<SubRealmBuilder, Error> {
1377 let name: String = name.into();
1378 let (child_realm_proxy, child_realm_server_end) = create_proxy::<ftest::RealmMarker>();
1379 self.realm_proxy
1380 .add_child_realm_from_relative_url(
1381 &name,
1382 &relative_url.into(),
1383 &options.into(),
1384 child_realm_server_end,
1385 )
1386 .await??;
1387
1388 let mut child_path = self.realm_path.clone();
1389 child_path.push(name);
1390 Ok(SubRealmBuilder {
1391 realm_proxy: child_realm_proxy,
1392 realm_path: child_path,
1393 local_component_runner_builder: self.local_component_runner_builder.clone(),
1394 })
1395 }
1396
1397 #[cfg(fuchsia_api_level_at_least = "26")]
1398 pub async fn add_child_realm_from_decl(
1399 &self,
1400 name: impl Into<String>,
1401 decl: cm_rust::ComponentDecl,
1402 options: ChildOptions,
1403 ) -> Result<SubRealmBuilder, Error> {
1404 let name: String = name.into();
1405 let (child_realm_proxy, child_realm_server_end) = create_proxy::<ftest::RealmMarker>();
1406 self.realm_proxy
1407 .add_child_realm_from_decl(
1408 &name,
1409 &decl.native_into_fidl(),
1410 &options.into(),
1411 child_realm_server_end,
1412 )
1413 .await??;
1414
1415 let mut child_path = self.realm_path.clone();
1416 child_path.push(name);
1417 Ok(SubRealmBuilder {
1418 realm_proxy: child_realm_proxy,
1419 realm_path: child_path,
1420 local_component_runner_builder: self.local_component_runner_builder.clone(),
1421 })
1422 }
1423
1424 pub async fn add_local_child<M>(
1426 &self,
1427 name: impl Into<String>,
1428 local_component_implementation: M,
1429 options: ChildOptions,
1430 ) -> Result<ChildRef, Error>
1431 where
1432 M: Fn(LocalComponentHandles) -> BoxFuture<'static, Result<(), anyhow::Error>>
1433 + Sync
1434 + Send
1435 + 'static,
1436 {
1437 let name: String = name.into();
1438 self.realm_proxy.add_local_child(&name, &options.into()).await??;
1439
1440 let mut child_path = self.realm_path.clone();
1441 child_path.push(name.clone());
1442 self.local_component_runner_builder
1443 .register_local_component(child_path.join("/"), local_component_implementation)
1444 .await?;
1445
1446 Ok(ChildRef::new(name, self.realm_path.clone()))
1447 }
1448
1449 pub async fn add_child(
1451 &self,
1452 name: impl Into<String>,
1453 url: impl Into<String>,
1454 options: ChildOptions,
1455 ) -> Result<ChildRef, Error> {
1456 let name: String = name.into();
1457 self.realm_proxy.add_child(&name, &url.into(), &options.into()).await??;
1458 Ok(ChildRef::new(name, self.realm_path.clone()))
1459 }
1460
1461 pub async fn add_child_from_decl(
1463 &self,
1464 name: impl Into<String>,
1465 decl: cm_rust::ComponentDecl,
1466 options: ChildOptions,
1467 ) -> Result<ChildRef, Error> {
1468 let name: String = name.into();
1469 self.realm_proxy
1470 .add_child_from_decl(&name, &decl.native_into_fidl(), &options.into())
1471 .await??;
1472 Ok(ChildRef::new(name, self.realm_path.clone()))
1473 }
1474
1475 pub async fn get_component_decl(
1477 &self,
1478 child_ref: impl Into<ChildRef>,
1479 ) -> Result<cm_rust::ComponentDecl, Error> {
1480 let child_ref: ChildRef = child_ref.into();
1481 child_ref.check_scope(&self.realm_path)?;
1482 let decl = self.realm_proxy.get_component_decl(&child_ref.name).await??;
1483 Ok(decl.fidl_into_native())
1484 }
1485
1486 pub async fn replace_component_decl(
1488 &self,
1489 child_ref: impl Into<ChildRef>,
1490 decl: cm_rust::ComponentDecl,
1491 ) -> Result<(), Error> {
1492 let child_ref: ChildRef = child_ref.into();
1493 child_ref.check_scope(&self.realm_path)?;
1494 self.realm_proxy
1495 .replace_component_decl(&child_ref.name, &decl.native_into_fidl())
1496 .await??;
1497 Ok(())
1498 }
1499
1500 pub async fn get_realm_decl(&self) -> Result<cm_rust::ComponentDecl, Error> {
1502 Ok(self.realm_proxy.get_realm_decl().await??.fidl_into_native())
1503 }
1504
1505 pub async fn replace_realm_decl(&self, decl: cm_rust::ComponentDecl) -> Result<(), Error> {
1507 self.realm_proxy.replace_realm_decl(&decl.native_into_fidl()).await?.map_err(Into::into)
1508 }
1509
1510 pub async fn init_mutable_config_from_package(
1512 &self,
1513 child_ref: impl Into<ChildRef>,
1514 ) -> Result<(), Error> {
1515 let child_ref = child_ref.into();
1516 child_ref.check_scope(&self.realm_path)?;
1517 self.realm_proxy
1518 .init_mutable_config_from_package(&child_ref.name)
1519 .await?
1520 .map_err(Into::into)
1521 }
1522
1523 pub async fn init_mutable_config_to_empty(
1525 &self,
1526 child_ref: impl Into<ChildRef>,
1527 ) -> Result<(), Error> {
1528 let child_ref = child_ref.into();
1529 child_ref.check_scope(&self.realm_path)?;
1530 self.realm_proxy.init_mutable_config_to_empty(&child_ref.name).await?.map_err(Into::into)
1531 }
1532
1533 pub async fn set_config_value(
1535 &self,
1536 child_ref: impl Into<ChildRef>,
1537 key: &str,
1538 value: cm_rust::ConfigValue,
1539 ) -> Result<(), Error> {
1540 let child_ref: ChildRef = child_ref.into();
1541 child_ref.check_scope(&self.realm_path)?;
1542 self.realm_proxy
1543 .set_config_value(
1544 &child_ref.name,
1545 key,
1546 &cm_rust::ConfigValueSpec { value }.native_into_fidl(),
1547 )
1548 .await?
1549 .map_err(Into::into)
1550 }
1551
1552 pub async fn add_route(&self, route: Route) -> Result<(), Error> {
1554 #[allow(unused_mut)] let mut capabilities = route.capabilities;
1556 if let Some(source) = &route.from {
1557 source.check_scope(&self.realm_path)?;
1558 }
1559 for target in &route.to {
1560 target.check_scope(&self.realm_path)?;
1561 }
1562 #[cfg(fuchsia_api_level_at_least = "25")]
1563 if let Some(from_dictionary) = route.from_dictionary {
1564 for c in &mut capabilities {
1565 match c {
1566 ftest::Capability::Protocol(c) => {
1567 c.from_dictionary = Some(from_dictionary.clone());
1568 }
1569 ftest::Capability::Directory(c) => {
1570 c.from_dictionary = Some(from_dictionary.clone());
1571 }
1572 ftest::Capability::Service(c) => {
1573 c.from_dictionary = Some(from_dictionary.clone());
1574 }
1575 ftest::Capability::Dictionary(c) => {
1576 c.from_dictionary = Some(from_dictionary.clone());
1577 }
1578 ftest::Capability::Resolver(c) => {
1579 c.from_dictionary = Some(from_dictionary.clone());
1580 }
1581 ftest::Capability::Runner(c) => {
1582 c.from_dictionary = Some(from_dictionary.clone());
1583 }
1584 ftest::Capability::Storage(_)
1585 | ftest::Capability::Config(_)
1586 | ftest::Capability::EventStream(_) => {
1587 return Err(Error::FromDictionaryNotSupported(c.clone()));
1588 }
1589 ftest::CapabilityUnknown!() => {}
1590 }
1591 }
1592 }
1593 if !capabilities.is_empty() {
1594 let route_targets = route.to.into_iter().map(Into::into).collect::<Vec<fdecl::Ref>>();
1595 let fut = self.realm_proxy.add_route(
1600 &capabilities,
1601 &route.from.ok_or(Error::MissingSource)?.into(),
1602 &route_targets,
1603 );
1604 fut.await??;
1605 }
1606 Ok(())
1607 }
1608
1609 pub async fn read_only_directory(
1613 &self,
1614 directory_name: impl Into<String>,
1615 to: Vec<impl Into<Ref>>,
1616 directory_contents: DirectoryContents,
1617 ) -> Result<(), Error> {
1618 let to: Vec<Ref> = to.into_iter().map(|t| t.into()).collect();
1619 for target in &to {
1620 target.check_scope(&self.realm_path)?;
1621 }
1622 let to = to.into_iter().map(Into::into).collect::<Vec<_>>();
1623
1624 let fut = self.realm_proxy.read_only_directory(
1625 &directory_name.into(),
1626 &to,
1627 directory_contents.into(),
1628 );
1629 fut.await??;
1630 Ok(())
1631 }
1632
1633 pub async fn add_capability(&self, capability: cm_rust::CapabilityDecl) -> Result<(), Error> {
1635 self.realm_proxy.add_capability(&capability.native_into_fidl()).await??;
1636 Ok(())
1637 }
1638
1639 #[cfg(fuchsia_api_level_at_least = "25")]
1640 pub async fn add_collection(
1642 &self,
1643 collection: cm_rust::CollectionDecl,
1644 ) -> Result<CollectionRef, Error> {
1645 let name = collection.name.clone().into();
1646 self.realm_proxy.add_collection(&collection.native_into_fidl()).await??;
1647 Ok(CollectionRef::new(name, self.realm_path.clone()))
1648 }
1649
1650 #[cfg(fuchsia_api_level_at_least = "25")]
1651 pub async fn add_environment(
1653 &self,
1654 environment: cm_rust::EnvironmentDecl,
1655 ) -> Result<(), Error> {
1656 self.realm_proxy.add_environment(&environment.native_into_fidl()).await??;
1657 Ok(())
1658 }
1659
1660 pub async fn with_nested_component_manager(
1670 &self,
1671 component_manager_fragment_only_url: &str,
1672 ) -> Result<(), Error> {
1673 self.realm_proxy
1674 .use_nested_component_manager(component_manager_fragment_only_url)
1675 .await?
1676 .map_err(Into::into)
1677 }
1678}
1679
1680pub struct DirectoryContents {
1683 contents: HashMap<String, fmem::Buffer>,
1684}
1685
1686impl DirectoryContents {
1687 pub fn new() -> Self {
1688 Self { contents: HashMap::new() }
1689 }
1690
1691 pub fn add_file(mut self, path: impl Into<String>, contents: impl Into<Vec<u8>>) -> Self {
1692 let contents: Vec<u8> = contents.into();
1693 let vmo = zx::Vmo::create(4096).expect("failed to create a VMO");
1694 vmo.write(&contents, 0).expect("failed to write to VMO");
1695 let buffer = fmem::Buffer { vmo, size: contents.len() as u64 };
1696 self.contents.insert(path.into(), buffer);
1697 self
1698 }
1699}
1700
1701impl Clone for DirectoryContents {
1702 fn clone(&self) -> Self {
1703 let mut new_self = Self::new();
1704 for (path, buf) in self.contents.iter() {
1705 new_self.contents.insert(
1706 path.clone(),
1707 fmem::Buffer {
1708 vmo: buf
1709 .vmo
1710 .create_child(zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE, 0, buf.size)
1711 .expect("failed to clone VMO"),
1712 size: buf.size,
1713 },
1714 );
1715 }
1716 new_self
1717 }
1718}
1719
1720impl From<DirectoryContents> for ftest::DirectoryContents {
1721 fn from(input: DirectoryContents) -> ftest::DirectoryContents {
1722 ftest::DirectoryContents {
1723 entries: input
1724 .contents
1725 .into_iter()
1726 .map(|(path, buf)| ftest::DirectoryEntry { file_path: path, file_contents: buf })
1727 .collect(),
1728 }
1729 }
1730}
1731
1732#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1735pub struct EventStream {
1736 name: String,
1737 scope: Option<Vec<Ref>>,
1738 rename: Option<String>,
1739 path: Option<String>,
1740}
1741
1742impl EventStream {
1743 pub fn new(name: impl Into<String>) -> Self {
1745 Self { name: name.into(), scope: None, rename: None, path: None }
1746 }
1747
1748 pub fn with_scope(mut self, scope: impl Into<Ref>) -> Self {
1751 self.scope.get_or_insert(vec![]).push(scope.into());
1752 self
1753 }
1754
1755 pub fn path(mut self, path: impl Into<String>) -> Self {
1760 self.path = Some(path.into());
1761 self
1762 }
1763
1764 pub fn as_(mut self, name: impl Into<String>) -> Self {
1766 self.rename = Some(name.into());
1767 self
1768 }
1769}
1770
1771#[derive(Debug, Clone)]
1773pub struct ChildOptions {
1774 startup: fdecl::StartupMode,
1775 environment: Option<String>,
1776 on_terminate: fdecl::OnTerminate,
1777 config_overrides: Option<Vec<fdecl::ConfigOverride>>,
1778}
1779
1780impl ChildOptions {
1781 pub fn new() -> Self {
1782 Self {
1783 startup: fdecl::StartupMode::Lazy,
1784 environment: None,
1785 on_terminate: fdecl::OnTerminate::None,
1786 config_overrides: None,
1787 }
1788 }
1789
1790 pub fn eager(mut self) -> Self {
1791 self.startup = fdecl::StartupMode::Eager;
1792 self
1793 }
1794
1795 pub fn environment(mut self, environment: impl Into<String>) -> Self {
1796 self.environment = Some(environment.into());
1797 self
1798 }
1799
1800 pub fn reboot_on_terminate(mut self) -> Self {
1801 self.on_terminate = fdecl::OnTerminate::Reboot;
1802 self
1803 }
1804
1805 pub fn config_overrides(
1806 mut self,
1807 config_overrides: impl Into<Vec<fdecl::ConfigOverride>>,
1808 ) -> Self {
1809 self.config_overrides = Some(config_overrides.into());
1810 self
1811 }
1812}
1813
1814impl Into<ftest::ChildOptions> for ChildOptions {
1815 fn into(self) -> ftest::ChildOptions {
1816 ftest::ChildOptions {
1817 startup: Some(self.startup),
1818 environment: self.environment,
1819 on_terminate: Some(self.on_terminate),
1820 config_overrides: self.config_overrides,
1821 ..Default::default()
1822 }
1823 }
1824}
1825
1826pub struct ScopedInstanceFactory {
1828 realm_proxy: Option<fcomponent::RealmProxy>,
1829 collection_name: String,
1830}
1831
1832impl ScopedInstanceFactory {
1833 pub fn new(collection_name: impl Into<String>) -> Self {
1835 ScopedInstanceFactory { realm_proxy: None, collection_name: collection_name.into() }
1836 }
1837
1838 pub fn with_realm_proxy(mut self, realm_proxy: fcomponent::RealmProxy) -> Self {
1842 self.realm_proxy = Some(realm_proxy);
1843 self
1844 }
1845
1846 pub async fn new_instance(
1849 &self,
1850 url: impl Into<String>,
1851 ) -> Result<ScopedInstance, anyhow::Error> {
1852 let id: u64 = rand::random();
1853 let child_name = format!("auto-{:x}", id);
1854 self.new_named_instance(child_name, url).await
1855 }
1856
1857 pub async fn new_named_instance(
1867 &self,
1868 child_name: impl Into<String>,
1869 url: impl Into<String>,
1870 ) -> Result<ScopedInstance, anyhow::Error> {
1871 let realm = if let Some(realm_proxy) = self.realm_proxy.as_ref() {
1872 realm_proxy.clone()
1873 } else {
1874 fclient::realm().context("Failed to connect to Realm service")?
1875 };
1876 let child_name = child_name.into();
1877 let collection_ref = fdecl::CollectionRef { name: self.collection_name.clone() };
1878 let child_decl = fdecl::Child {
1879 name: Some(child_name.clone()),
1880 url: Some(url.into()),
1881 startup: Some(fdecl::StartupMode::Lazy),
1882 ..Default::default()
1883 };
1884 let (controller_proxy, controller) = create_proxy::<fcomponent::ControllerMarker>();
1885 let child_args = fcomponent::CreateChildArgs {
1886 numbered_handles: None,
1887 controller: Some(controller),
1888 ..Default::default()
1889 };
1890 let () = realm
1891 .create_child(&collection_ref, &child_decl, child_args)
1892 .await
1893 .context("CreateChild FIDL failed.")?
1894 .map_err(|e| format_err!("Failed to create child: {:?}", e))?;
1895 let child_ref = fdecl::ChildRef {
1896 name: child_name.clone(),
1897 collection: Some(self.collection_name.clone()),
1898 };
1899 let (exposed_dir, server) = endpoints::create_proxy::<fio::DirectoryMarker>();
1900 let () = realm
1901 .open_exposed_dir(&child_ref, server)
1902 .await
1903 .context("OpenExposedDir FIDL failed.")?
1904 .map_err(|e|
1905 format_err!("Failed to open exposed dir of child: {:?}", e))?;
1909 Ok(ScopedInstance {
1910 realm,
1911 child_name,
1912 collection: self.collection_name.clone(),
1913 exposed_dir,
1914 destroy_channel: None,
1915 controller_proxy,
1916 })
1917 }
1918}
1919
1920#[must_use = "Dropping `ScopedInstance` will cause the component instance to be stopped and destroyed."]
1923pub struct ScopedInstance {
1924 realm: fcomponent::RealmProxy,
1925 child_name: String,
1926 collection: String,
1927 exposed_dir: fio::DirectoryProxy,
1928 destroy_channel: Option<
1929 futures::channel::oneshot::Sender<
1930 Result<
1931 fidl::client::QueryResponseFut<fcomponent::RealmDestroyChildResult>,
1932 anyhow::Error,
1933 >,
1934 >,
1935 >,
1936 controller_proxy: fcomponent::ControllerProxy,
1937}
1938
1939impl ScopedInstance {
1940 pub async fn new(coll: String, url: String) -> Result<Self, anyhow::Error> {
1943 ScopedInstanceFactory::new(coll).new_instance(url).await
1944 }
1945
1946 pub async fn new_with_name(
1952 child_name: String,
1953 collection: String,
1954 url: String,
1955 ) -> Result<Self, anyhow::Error> {
1956 ScopedInstanceFactory::new(collection).new_named_instance(child_name, url).await
1957 }
1958
1959 pub async fn is_started(&self) -> Result<bool, anyhow::Error> {
1961 Ok(self
1962 .controller_proxy
1963 .is_started()
1964 .await
1965 .context("failed to use controller proxy")?
1966 .map_err(|e| format_err!("failed to determine if component is started: {:?}", e))?)
1967 }
1968
1969 pub async fn start(&self) -> Result<ExecutionController, anyhow::Error> {
1971 self.start_with_args(fcomponent::StartChildArgs::default()).await
1972 }
1973
1974 pub async fn start_with_args(
1977 &self,
1978 args: fcomponent::StartChildArgs,
1979 ) -> Result<ExecutionController, anyhow::Error> {
1980 let (execution_proxy, execution_server_end) =
1981 create_proxy::<fcomponent::ExecutionControllerMarker>();
1982 self.controller_proxy
1983 .start(args, execution_server_end)
1984 .await
1985 .context("failed to use controller proxy")?
1986 .map_err(|e| format_err!("failed to start component: {:?}", e))?;
1987 Ok(ExecutionController::new(execution_proxy))
1988 }
1989
1990 pub fn controller(&self) -> &fcomponent::ControllerProxy {
1991 &self.controller_proxy
1992 }
1993
1994 pub fn connect_to_binder(&self) -> Result<fcomponent::BinderProxy, anyhow::Error> {
1999 let binder: fcomponent::BinderProxy = self
2000 .connect_to_protocol_at_exposed_dir()
2001 .context("failed to connect to fuchsia.component.Binder")?;
2002
2003 Ok(binder)
2004 }
2005
2006 pub async fn start_with_binder_sync(&self) -> Result<(), anyhow::Error> {
2014 let mut event_stream = component_events::events::EventStream::open()
2015 .await
2016 .context("failed to create EventSource")?;
2017
2018 let _: ClientEnd<fcomponent::BinderMarker> = self
2019 .connect_to_protocol_at_exposed_dir()
2020 .context("failed to connect to fuchsia.component.Binder")?;
2021
2022 let _ = EventMatcher::ok()
2023 .moniker(self.moniker())
2024 .wait::<Started>(&mut event_stream)
2025 .await
2026 .context("failed to observe Started event")?;
2027
2028 Ok(())
2029 }
2030
2031 pub fn connect_to_protocol_at_exposed_dir<T: fclient::Connect>(
2033 &self,
2034 ) -> Result<T, anyhow::Error> {
2035 T::connect_at_dir_root(&self.exposed_dir)
2036 }
2037
2038 pub fn connect_to_named_protocol_at_exposed_dir<P: DiscoverableProtocolMarker>(
2040 &self,
2041 protocol_name: &str,
2042 ) -> Result<P::Proxy, anyhow::Error> {
2043 fclient::connect_to_named_protocol_at_dir_root::<P>(&self.exposed_dir, protocol_name)
2044 }
2045
2046 pub fn connect_request_to_protocol_at_exposed_dir<P: DiscoverableProtocolMarker>(
2049 &self,
2050 server_end: ServerEnd<P>,
2051 ) -> Result<(), anyhow::Error> {
2052 self.connect_request_to_named_protocol_at_exposed_dir(
2053 P::PROTOCOL_NAME,
2054 server_end.into_channel(),
2055 )
2056 }
2057
2058 pub fn connect_request_to_named_protocol_at_exposed_dir(
2061 &self,
2062 protocol_name: &str,
2063 server_end: zx::Channel,
2064 ) -> Result<(), anyhow::Error> {
2065 self.exposed_dir
2066 .open(protocol_name, fio::Flags::PROTOCOL_SERVICE, &Default::default(), server_end)
2067 .map_err(Into::into)
2068 }
2069
2070 pub fn get_exposed_dir(&self) -> &fio::DirectoryProxy {
2072 &self.exposed_dir
2073 }
2074
2075 pub fn destroy_waiter_taken(&self) -> bool {
2077 self.destroy_channel.is_some()
2078 }
2079
2080 pub fn take_destroy_waiter(
2083 &mut self,
2084 ) -> impl futures::Future<Output = Result<(), anyhow::Error>> {
2085 if self.destroy_channel.is_some() {
2086 panic!("destroy waiter already taken");
2087 }
2088 let (sender, receiver) = futures::channel::oneshot::channel();
2089 self.destroy_channel = Some(sender);
2090 receiver.err_into().and_then(futures::future::ready).and_then(
2091 |fidl_fut: fidl::client::QueryResponseFut<_>| {
2092 fidl_fut.map(|r: Result<Result<(), fidl_fuchsia_component::Error>, fidl::Error>| {
2093 r.context("DestroyChild FIDL error")?
2094 .map_err(|e| format_err!("Failed to destroy child: {:?}", e))
2095 })
2096 },
2097 )
2098 }
2099
2100 pub fn child_name(&self) -> &str {
2102 self.child_name.as_str()
2103 }
2104
2105 pub fn moniker(&self) -> String {
2107 format!("./{}:{}", self.collection, self.child_name)
2108 }
2109}
2110
2111impl Drop for ScopedInstance {
2112 fn drop(&mut self) {
2113 let Self { realm, collection, child_name, destroy_channel, .. } = self;
2114 let child_ref =
2115 fdecl::ChildRef { name: child_name.clone(), collection: Some(collection.clone()) };
2116 let result = Ok(realm.destroy_child(&child_ref));
2122 if let Some(chan) = destroy_channel.take() {
2123 let () = chan.send(result).unwrap_or_else(|result| {
2124 warn!("Failed to send result for destroyed scoped instance. Result={:?}", result);
2125 });
2126 }
2127 }
2128}
2129
2130pub struct ExecutionController {
2135 execution_proxy: fcomponent::ExecutionControllerProxy,
2136 execution_event_stream: fcomponent::ExecutionControllerEventStream,
2137}
2138
2139impl ExecutionController {
2140 fn new(execution_proxy: fcomponent::ExecutionControllerProxy) -> Self {
2141 let execution_event_stream = execution_proxy.take_event_stream();
2142 Self { execution_proxy, execution_event_stream }
2143 }
2144
2145 pub async fn stop(self) -> Result<fcomponent::StoppedPayload, anyhow::Error> {
2148 let _ = self.execution_proxy.stop();
2152 self.wait_for_stop().await
2153 }
2154
2155 pub async fn wait_for_stop(mut self) -> Result<fcomponent::StoppedPayload, anyhow::Error> {
2157 loop {
2158 match self.execution_event_stream.try_next().await {
2159 Ok(Some(fcomponent::ExecutionControllerEvent::OnStop { stopped_payload })) => {
2160 return Ok(stopped_payload);
2161 }
2162 Ok(Some(fcomponent::ExecutionControllerEvent::_UnknownEvent {
2163 ordinal, ..
2164 })) => {
2165 warn!(ordinal:%; "fuchsia.component/ExecutionController delivered unknown event");
2166 }
2167 Ok(None) => {
2168 return Err(format_err!("ExecutionController closed and no OnStop received"));
2169 }
2170 Err(e) => {
2171 return Err(format_err!("failed to wait for OnStop: {:?}", e));
2172 }
2173 }
2174 }
2175 }
2176}
2177
2178#[cfg(test)]
2179mod tests {
2180 use super::*;
2181 use assert_matches::assert_matches;
2182 use fidl::endpoints::create_proxy_and_stream;
2183 use fidl_fuchsia_component as fcomponent;
2184 use futures::channel::mpsc;
2185 use futures::future::pending;
2186 use futures::{SinkExt, StreamExt};
2187
2188 #[fuchsia::test]
2193 fn child_options_to_fidl() {
2194 let options: ftest::ChildOptions = ChildOptions::new().into();
2195 assert_eq!(
2196 options,
2197 ftest::ChildOptions {
2198 startup: Some(fdecl::StartupMode::Lazy),
2200 on_terminate: Some(fdecl::OnTerminate::None),
2201 ..Default::default()
2202 },
2203 );
2204 assert_eq!(
2205 options,
2206 ftest::ChildOptions {
2207 startup: Some(fdecl::StartupMode::Lazy),
2208 environment: None,
2209 on_terminate: Some(fdecl::OnTerminate::None),
2210 config_overrides: None,
2211 __source_breaking: fidl::marker::SourceBreaking,
2212 },
2213 );
2214 let options: ftest::ChildOptions = ChildOptions::new().eager().into();
2215 assert_eq!(
2216 options,
2217 ftest::ChildOptions {
2218 startup: Some(fdecl::StartupMode::Eager),
2219 environment: None,
2220 on_terminate: Some(fdecl::OnTerminate::None),
2221 config_overrides: None,
2222 __source_breaking: fidl::marker::SourceBreaking,
2223 },
2224 );
2225 let options: ftest::ChildOptions = ChildOptions::new().environment("test_env").into();
2226 assert_eq!(
2227 options,
2228 ftest::ChildOptions {
2229 startup: Some(fdecl::StartupMode::Lazy),
2230 environment: Some("test_env".to_string()),
2231 on_terminate: Some(fdecl::OnTerminate::None),
2232 config_overrides: None,
2233 __source_breaking: fidl::marker::SourceBreaking,
2234 },
2235 );
2236 let options: ftest::ChildOptions = ChildOptions::new().reboot_on_terminate().into();
2237 assert_eq!(
2238 options,
2239 ftest::ChildOptions {
2240 startup: Some(fdecl::StartupMode::Lazy),
2241 environment: None,
2242 on_terminate: Some(fdecl::OnTerminate::Reboot),
2243 config_overrides: None,
2244 __source_breaking: fidl::marker::SourceBreaking,
2245 },
2246 );
2247
2248 let mut config_overrides: Vec<fdecl::ConfigOverride> = vec![];
2249 config_overrides.push(fdecl::ConfigOverride {
2250 key: Some("mystring".to_string()),
2251 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::String(
2252 "Fuchsia".to_string(),
2253 ))),
2254 ..Default::default()
2255 });
2256 config_overrides.push(fdecl::ConfigOverride {
2257 key: Some("mynumber".to_string()),
2258 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Uint64(200))),
2259 ..Default::default()
2260 });
2261 let options: ftest::ChildOptions =
2262 ChildOptions::new().config_overrides(config_overrides.clone()).into();
2263 assert_eq!(
2264 options,
2265 ftest::ChildOptions {
2266 startup: Some(fdecl::StartupMode::Lazy),
2267 environment: None,
2268 on_terminate: Some(fdecl::OnTerminate::None),
2269 config_overrides: Some(config_overrides),
2270 __source_breaking: fidl::marker::SourceBreaking,
2271 },
2272 );
2273 }
2274
2275 #[fuchsia::test]
2276 async fn child_scope_prevents_cross_realm_usage() {
2277 let (builder, _server_task, receive_server_requests) = new_realm_builder_and_server_task();
2278 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
2279 let child_realm_b = builder.add_child_realm("b", ChildOptions::new()).await.unwrap();
2280 let child_c = child_realm_b.add_child("c", "test://c", ChildOptions::new()).await.unwrap();
2281
2282 assert_matches!(
2283 builder.add_route(
2284 Route::new()
2285 .capability(Capability::protocol::<fcomponent::RealmMarker>())
2286 .from(&child_a)
2287 .to(&child_c)
2288 ).await,
2289 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_c).into()
2290 );
2291
2292 assert_matches!(
2293 child_realm_b.add_route(
2294 Route::new()
2295 .capability(Capability::protocol::<fcomponent::RealmMarker>())
2296 .from(&child_a)
2297 .to(&child_c)
2298 ).await,
2299 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_a).into()
2300 );
2301
2302 assert_matches!(
2303 builder.get_component_decl(&child_c).await,
2304 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_c).into()
2305 );
2306
2307 assert_matches!(
2308 child_realm_b.get_component_decl(&child_a).await,
2309 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_a).into()
2310 );
2311
2312 assert_matches!(
2313 builder.replace_component_decl(&child_c, cm_rust::ComponentDecl::default()).await,
2314 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_c).into()
2315 );
2316
2317 assert_matches!(
2318 child_realm_b.replace_component_decl(&child_a, cm_rust::ComponentDecl::default()).await,
2319 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_a).into()
2320 );
2321
2322 let sub_realm_receiver = confirm_num_server_requests(receive_server_requests, 2).remove(0);
2325 confirm_num_server_requests(sub_realm_receiver, 1);
2326 }
2327
2328 #[fuchsia::test]
2329 async fn child_ref_construction() {
2330 let (builder, _server_task, receive_server_requests) = new_realm_builder_and_server_task();
2331 let child_realm_a = builder.add_child_realm("a", ChildOptions::new()).await.unwrap();
2332 let child_realm_b = child_realm_a.add_child_realm("b", ChildOptions::new()).await.unwrap();
2333
2334 let child_ref_a: ChildRef = (&child_realm_a).into();
2335 let child_ref_b: ChildRef = (&child_realm_b).into();
2336
2337 assert_eq!(child_ref_a, ChildRef::new("a".to_string(), vec![]),);
2338
2339 assert_eq!(child_ref_b, ChildRef::new("b".to_string(), vec!["a".to_string()]),);
2340
2341 let child_ref_c = builder.add_child("c", "test://c", ChildOptions::new()).await.unwrap();
2342 let child_ref_d =
2343 child_realm_a.add_child("d", "test://d", ChildOptions::new()).await.unwrap();
2344 let child_ref_e =
2345 child_realm_b.add_child("e", "test://e", ChildOptions::new()).await.unwrap();
2346
2347 assert_eq!(child_ref_c, ChildRef::new("c".to_string(), vec![]),);
2348
2349 assert_eq!(child_ref_d, ChildRef::new("d".to_string(), vec!["a".to_string()]),);
2350
2351 assert_eq!(
2352 child_ref_e,
2353 ChildRef::new("e".to_string(), vec!["a".to_string(), "b".to_string()]),
2354 );
2355
2356 confirm_num_server_requests(receive_server_requests, 2);
2359 }
2360
2361 #[fuchsia::test]
2362 async fn protocol_capability_construction() {
2363 assert_eq!(
2364 Capability::protocol_by_name("test"),
2365 ProtocolCapability {
2366 name: "test".to_string(),
2367 as_: None,
2368 type_: fdecl::DependencyType::Strong,
2369 path: None,
2370 availability: None,
2371 },
2372 );
2373 assert_eq!(
2374 Capability::protocol::<ftest::RealmBuilderFactoryMarker>(),
2375 ProtocolCapability {
2376 name: ftest::RealmBuilderFactoryMarker::PROTOCOL_NAME.to_string(),
2377 as_: None,
2378 type_: fdecl::DependencyType::Strong,
2379 path: None,
2380 availability: None,
2381 },
2382 );
2383 assert_eq!(
2384 Capability::protocol_by_name("test").as_("test2"),
2385 ProtocolCapability {
2386 name: "test".to_string(),
2387 as_: Some("test2".to_string()),
2388 type_: fdecl::DependencyType::Strong,
2389 path: None,
2390 availability: None,
2391 },
2392 );
2393 assert_eq!(
2394 Capability::protocol_by_name("test").weak(),
2395 ProtocolCapability {
2396 name: "test".to_string(),
2397 as_: None,
2398 type_: fdecl::DependencyType::Weak,
2399 path: None,
2400 availability: None,
2401 },
2402 );
2403 assert_eq!(
2404 Capability::protocol_by_name("test").path("/svc/test2"),
2405 ProtocolCapability {
2406 name: "test".to_string(),
2407 as_: None,
2408 type_: fdecl::DependencyType::Strong,
2409 path: Some("/svc/test2".to_string()),
2410 availability: None,
2411 },
2412 );
2413 assert_eq!(
2414 Capability::protocol_by_name("test").optional(),
2415 ProtocolCapability {
2416 name: "test".to_string(),
2417 as_: None,
2418 type_: fdecl::DependencyType::Strong,
2419 path: None,
2420 availability: Some(fdecl::Availability::Optional),
2421 },
2422 );
2423 assert_eq!(
2424 Capability::protocol_by_name("test").availability_same_as_target(),
2425 ProtocolCapability {
2426 name: "test".to_string(),
2427 as_: None,
2428 type_: fdecl::DependencyType::Strong,
2429 path: None,
2430 availability: Some(fdecl::Availability::SameAsTarget),
2431 },
2432 );
2433 }
2434
2435 #[fuchsia::test]
2436 async fn directory_capability_construction() {
2437 assert_eq!(
2438 Capability::directory("test"),
2439 DirectoryCapability {
2440 name: "test".to_string(),
2441 as_: None,
2442 type_: fdecl::DependencyType::Strong,
2443 rights: None,
2444 subdir: None,
2445 path: None,
2446 availability: None,
2447 },
2448 );
2449 assert_eq!(
2450 Capability::directory("test").as_("test2"),
2451 DirectoryCapability {
2452 name: "test".to_string(),
2453 as_: Some("test2".to_string()),
2454 type_: fdecl::DependencyType::Strong,
2455 rights: None,
2456 subdir: None,
2457 path: None,
2458 availability: None,
2459 },
2460 );
2461 assert_eq!(
2462 Capability::directory("test").weak(),
2463 DirectoryCapability {
2464 name: "test".to_string(),
2465 as_: None,
2466 type_: fdecl::DependencyType::Weak,
2467 rights: None,
2468 subdir: None,
2469 path: None,
2470 availability: None,
2471 },
2472 );
2473 assert_eq!(
2474 Capability::directory("test").rights(fio::RX_STAR_DIR),
2475 DirectoryCapability {
2476 name: "test".to_string(),
2477 as_: None,
2478 type_: fdecl::DependencyType::Strong,
2479 rights: Some(fio::RX_STAR_DIR),
2480 subdir: None,
2481 path: None,
2482 availability: None,
2483 },
2484 );
2485 assert_eq!(
2486 Capability::directory("test").subdir("test2"),
2487 DirectoryCapability {
2488 name: "test".to_string(),
2489 as_: None,
2490 type_: fdecl::DependencyType::Strong,
2491 rights: None,
2492 subdir: Some("test2".to_string()),
2493 path: None,
2494 availability: None,
2495 },
2496 );
2497 assert_eq!(
2498 Capability::directory("test").path("/test2"),
2499 DirectoryCapability {
2500 name: "test".to_string(),
2501 as_: None,
2502 type_: fdecl::DependencyType::Strong,
2503 rights: None,
2504 subdir: None,
2505 path: Some("/test2".to_string()),
2506 availability: None,
2507 },
2508 );
2509 assert_eq!(
2510 Capability::directory("test").optional(),
2511 DirectoryCapability {
2512 name: "test".to_string(),
2513 as_: None,
2514 type_: fdecl::DependencyType::Strong,
2515 rights: None,
2516 subdir: None,
2517 path: None,
2518 availability: Some(fdecl::Availability::Optional),
2519 },
2520 );
2521 assert_eq!(
2522 Capability::directory("test").availability_same_as_target(),
2523 DirectoryCapability {
2524 name: "test".to_string(),
2525 as_: None,
2526 type_: fdecl::DependencyType::Strong,
2527 rights: None,
2528 subdir: None,
2529 path: None,
2530 availability: Some(fdecl::Availability::SameAsTarget),
2531 },
2532 );
2533 }
2534
2535 #[fuchsia::test]
2536 async fn storage_capability_construction() {
2537 assert_eq!(
2538 Capability::storage("test"),
2539 StorageCapability {
2540 name: "test".to_string(),
2541 as_: None,
2542 path: None,
2543 availability: None
2544 },
2545 );
2546 assert_eq!(
2547 Capability::storage("test").as_("test2"),
2548 StorageCapability {
2549 name: "test".to_string(),
2550 as_: Some("test2".to_string()),
2551 path: None,
2552 availability: None,
2553 },
2554 );
2555 assert_eq!(
2556 Capability::storage("test").path("/test2"),
2557 StorageCapability {
2558 name: "test".to_string(),
2559 as_: None,
2560 path: Some("/test2".to_string()),
2561 availability: None,
2562 },
2563 );
2564 assert_eq!(
2565 Capability::storage("test").optional(),
2566 StorageCapability {
2567 name: "test".to_string(),
2568 as_: None,
2569 path: None,
2570 availability: Some(fdecl::Availability::Optional),
2571 },
2572 );
2573 assert_eq!(
2574 Capability::storage("test").availability_same_as_target(),
2575 StorageCapability {
2576 name: "test".to_string(),
2577 as_: None,
2578 path: None,
2579 availability: Some(fdecl::Availability::SameAsTarget),
2580 },
2581 );
2582 }
2583
2584 #[fuchsia::test]
2585 async fn service_capability_construction() {
2586 assert_eq!(
2587 Capability::service_by_name("test"),
2588 ServiceCapability {
2589 name: "test".to_string(),
2590 as_: None,
2591 path: None,
2592 availability: None
2593 },
2594 );
2595 assert_eq!(
2596 Capability::service_by_name("test").as_("test2"),
2597 ServiceCapability {
2598 name: "test".to_string(),
2599 as_: Some("test2".to_string()),
2600 path: None,
2601 availability: None,
2602 },
2603 );
2604 assert_eq!(
2605 Capability::service_by_name("test").path("/svc/test2"),
2606 ServiceCapability {
2607 name: "test".to_string(),
2608 as_: None,
2609 path: Some("/svc/test2".to_string()),
2610 availability: None,
2611 },
2612 );
2613 assert_eq!(
2614 Capability::service_by_name("test").optional(),
2615 ServiceCapability {
2616 name: "test".to_string(),
2617 as_: None,
2618 path: None,
2619 availability: Some(fdecl::Availability::Optional),
2620 },
2621 );
2622 assert_eq!(
2623 Capability::service_by_name("test").availability_same_as_target(),
2624 ServiceCapability {
2625 name: "test".to_string(),
2626 as_: None,
2627 path: None,
2628 availability: Some(fdecl::Availability::SameAsTarget),
2629 },
2630 );
2631 }
2632
2633 #[fuchsia::test]
2634 async fn dictionary_capability_construction() {
2635 assert_eq!(
2636 Capability::dictionary("test"),
2637 DictionaryCapability { name: "test".to_string(), as_: None, availability: None },
2638 );
2639 assert_eq!(
2640 Capability::dictionary("test").as_("test2"),
2641 DictionaryCapability {
2642 name: "test".to_string(),
2643 as_: Some("test2".to_string()),
2644 availability: None,
2645 },
2646 );
2647 assert_eq!(
2648 Capability::dictionary("test").optional(),
2649 DictionaryCapability {
2650 name: "test".to_string(),
2651 as_: None,
2652 availability: Some(fdecl::Availability::Optional),
2653 },
2654 );
2655 assert_eq!(
2656 Capability::dictionary("test").availability_same_as_target(),
2657 DictionaryCapability {
2658 name: "test".to_string(),
2659 as_: None,
2660 availability: Some(fdecl::Availability::SameAsTarget),
2661 },
2662 );
2663 }
2664
2665 #[fuchsia::test]
2666 async fn route_construction() {
2667 assert_eq!(
2668 Route::new()
2669 .capability(Capability::protocol_by_name("test"))
2670 .capability(Capability::protocol_by_name("test2"))
2671 .from(Ref::child("a"))
2672 .to(Ref::collection("b"))
2673 .to(Ref::parent()),
2674 Route {
2675 capabilities: vec![
2676 Capability::protocol_by_name("test").into(),
2677 Capability::protocol_by_name("test2").into(),
2678 ],
2679 from: Some(Ref::child("a").into()),
2680 from_dictionary: None,
2681 to: vec![Ref::collection("b").into(), Ref::parent().into(),],
2682 },
2683 );
2684 }
2685
2686 #[derive(Debug)]
2687 enum ServerRequest {
2688 AddChild {
2689 name: String,
2690 url: String,
2691 options: ftest::ChildOptions,
2692 },
2693 AddChildFromDecl {
2694 name: String,
2695 decl: fdecl::Component,
2696 options: ftest::ChildOptions,
2697 },
2698 AddLocalChild {
2699 name: String,
2700 options: ftest::ChildOptions,
2701 },
2702 AddChildRealm {
2703 name: String,
2704 options: ftest::ChildOptions,
2705 receive_requests: mpsc::UnboundedReceiver<ServerRequest>,
2706 },
2707 AddChildRealmFromRelativeUrl {
2708 name: String,
2709 relative_url: String,
2710 options: ftest::ChildOptions,
2711 receive_requests: mpsc::UnboundedReceiver<ServerRequest>,
2712 },
2713 AddChildRealmFromDecl {
2714 name: String,
2715 decl: fdecl::Component,
2716 options: ftest::ChildOptions,
2717 receive_requests: mpsc::UnboundedReceiver<ServerRequest>,
2718 },
2719 GetComponentDecl {
2720 name: String,
2721 },
2722 ReplaceComponentDecl {
2723 name: String,
2724 component_decl: fdecl::Component,
2725 },
2726 UseNestedComponentManager {
2727 #[allow(unused)]
2728 component_manager_relative_url: String,
2729 },
2730 GetRealmDecl,
2731 ReplaceRealmDecl {
2732 component_decl: fdecl::Component,
2733 },
2734 AddRoute {
2735 capabilities: Vec<ftest::Capability>,
2736 from: fdecl::Ref,
2737 to: Vec<fdecl::Ref>,
2738 },
2739 ReadOnlyDirectory {
2740 name: String,
2741 to: Vec<fdecl::Ref>,
2742 },
2743 InitMutableConfigFromPackage {
2744 name: String,
2745 },
2746 InitMutableConfigToEmpty {
2747 name: String,
2748 },
2749 AddCapability {
2750 capability: fdecl::Capability,
2751 },
2752 AddCollection {
2753 collection: fdecl::Collection,
2754 },
2755 AddEnvironment {
2756 environment: fdecl::Environment,
2757 },
2758 SetConfigValue {
2759 name: String,
2760 key: String,
2761 value: fdecl::ConfigValueSpec,
2762 },
2763 }
2764
2765 fn handle_realm_stream(
2766 mut stream: ftest::RealmRequestStream,
2767 mut report_requests: mpsc::UnboundedSender<ServerRequest>,
2768 ) -> BoxFuture<'static, ()> {
2769 async move {
2770 let mut child_realm_streams = vec![];
2771 while let Some(req) = stream.try_next().await.unwrap() {
2772 match req {
2773 ftest::RealmRequest::AddChild { responder, name, url, options } => {
2774 report_requests
2775 .send(ServerRequest::AddChild { name, url, options })
2776 .await
2777 .unwrap();
2778 responder.send(Ok(())).unwrap();
2779 }
2780 ftest::RealmRequest::AddChildFromDecl { responder, name, decl, options } => {
2781 report_requests
2782 .send(ServerRequest::AddChildFromDecl { name, decl, options })
2783 .await
2784 .unwrap();
2785 responder.send(Ok(())).unwrap();
2786 }
2787 ftest::RealmRequest::AddLocalChild { responder, name, options } => {
2788 report_requests
2789 .send(ServerRequest::AddLocalChild { name, options })
2790 .await
2791 .unwrap();
2792 responder.send(Ok(())).unwrap();
2793 }
2794 ftest::RealmRequest::AddChildRealm {
2795 responder,
2796 child_realm,
2797 name,
2798 options,
2799 } => {
2800 let (child_realm_report_requests, receive_requests) = mpsc::unbounded();
2801
2802 report_requests
2803 .send(ServerRequest::AddChildRealm { name, options, receive_requests })
2804 .await
2805 .unwrap();
2806
2807 let child_realm_stream = child_realm.into_stream();
2808 child_realm_streams.push(fasync::Task::spawn(async move {
2809 handle_realm_stream(child_realm_stream, child_realm_report_requests)
2810 .await
2811 }));
2812 responder.send(Ok(())).unwrap();
2813 }
2814 ftest::RealmRequest::AddChildRealmFromRelativeUrl {
2815 responder,
2816 child_realm,
2817 name,
2818 relative_url,
2819 options,
2820 } => {
2821 let (child_realm_report_requests, receive_requests) = mpsc::unbounded();
2822
2823 report_requests
2824 .send(ServerRequest::AddChildRealmFromRelativeUrl {
2825 name,
2826 relative_url,
2827 options,
2828 receive_requests,
2829 })
2830 .await
2831 .unwrap();
2832
2833 let child_realm_stream = child_realm.into_stream();
2834 child_realm_streams.push(fasync::Task::spawn(async move {
2835 handle_realm_stream(child_realm_stream, child_realm_report_requests)
2836 .await
2837 }));
2838 responder.send(Ok(())).unwrap();
2839 }
2840 ftest::RealmRequest::AddChildRealmFromDecl {
2841 name,
2842 decl,
2843 options,
2844 child_realm,
2845 responder,
2846 } => {
2847 let (child_realm_report_requests, receive_requests) = mpsc::unbounded();
2848
2849 report_requests
2850 .send(ServerRequest::AddChildRealmFromDecl {
2851 name,
2852 decl,
2853 options,
2854 receive_requests,
2855 })
2856 .await
2857 .unwrap();
2858
2859 let child_realm_stream = child_realm.into_stream();
2860 child_realm_streams.push(fasync::Task::spawn(async move {
2861 handle_realm_stream(child_realm_stream, child_realm_report_requests)
2862 .await
2863 }));
2864 responder.send(Ok(())).unwrap();
2865 }
2866 ftest::RealmRequest::GetComponentDecl { responder, name } => {
2867 report_requests
2868 .send(ServerRequest::GetComponentDecl { name })
2869 .await
2870 .unwrap();
2871 responder.send(Ok(&fdecl::Component::default())).unwrap();
2872 }
2873 ftest::RealmRequest::UseNestedComponentManager {
2874 responder,
2875 component_manager_relative_url,
2876 } => {
2877 report_requests
2878 .send(ServerRequest::UseNestedComponentManager {
2879 component_manager_relative_url,
2880 })
2881 .await
2882 .unwrap();
2883 responder.send(Ok(())).unwrap();
2884 }
2885 ftest::RealmRequest::ReplaceComponentDecl {
2886 responder,
2887 name,
2888 component_decl,
2889 } => {
2890 report_requests
2891 .send(ServerRequest::ReplaceComponentDecl { name, component_decl })
2892 .await
2893 .unwrap();
2894 responder.send(Ok(())).unwrap();
2895 }
2896 ftest::RealmRequest::GetRealmDecl { responder } => {
2897 report_requests.send(ServerRequest::GetRealmDecl).await.unwrap();
2898 responder.send(Ok(&fdecl::Component::default())).unwrap();
2899 }
2900 ftest::RealmRequest::ReplaceRealmDecl { responder, component_decl } => {
2901 report_requests
2902 .send(ServerRequest::ReplaceRealmDecl { component_decl })
2903 .await
2904 .unwrap();
2905 responder.send(Ok(())).unwrap();
2906 }
2907 ftest::RealmRequest::AddRoute { responder, capabilities, from, to } => {
2908 report_requests
2909 .send(ServerRequest::AddRoute { capabilities, from, to })
2910 .await
2911 .unwrap();
2912 responder.send(Ok(())).unwrap();
2913 }
2914 ftest::RealmRequest::ReadOnlyDirectory { responder, name, to, .. } => {
2915 report_requests
2916 .send(ServerRequest::ReadOnlyDirectory { name, to })
2917 .await
2918 .unwrap();
2919 responder.send(Ok(())).unwrap();
2920 }
2921 ftest::RealmRequest::InitMutableConfigFromPackage { name, responder } => {
2922 report_requests
2923 .send(ServerRequest::InitMutableConfigFromPackage { name })
2924 .await
2925 .unwrap();
2926 responder.send(Ok(())).unwrap();
2927 }
2928 ftest::RealmRequest::InitMutableConfigToEmpty { name, responder } => {
2929 report_requests
2930 .send(ServerRequest::InitMutableConfigToEmpty { name })
2931 .await
2932 .unwrap();
2933 responder.send(Ok(())).unwrap();
2934 }
2935 ftest::RealmRequest::AddCapability { capability, responder } => {
2936 report_requests
2937 .send(ServerRequest::AddCapability { capability })
2938 .await
2939 .unwrap();
2940 responder.send(Ok(())).unwrap();
2941 }
2942 ftest::RealmRequest::AddCollection { collection, responder } => {
2943 report_requests
2944 .send(ServerRequest::AddCollection { collection })
2945 .await
2946 .unwrap();
2947 responder.send(Ok(())).unwrap();
2948 }
2949 ftest::RealmRequest::AddEnvironment { environment, responder } => {
2950 report_requests
2951 .send(ServerRequest::AddEnvironment { environment })
2952 .await
2953 .unwrap();
2954 responder.send(Ok(())).unwrap();
2955 }
2956 ftest::RealmRequest::SetConfigValue { responder, name, key, value } => {
2957 report_requests
2958 .send(ServerRequest::SetConfigValue { name, key, value })
2959 .await
2960 .unwrap();
2961 responder.send(Ok(())).unwrap();
2962 }
2963 }
2964 }
2965 }
2966 .boxed()
2967 }
2968
2969 fn new_realm_builder_and_server_task(
2970 ) -> (RealmBuilder, fasync::Task<()>, mpsc::UnboundedReceiver<ServerRequest>) {
2971 let (realm_proxy, realm_stream) = create_proxy_and_stream::<ftest::RealmMarker>();
2972 let (builder_proxy, mut builder_stream) = create_proxy_and_stream::<ftest::BuilderMarker>();
2973
2974 let builder_task = fasync::Task::spawn(async move {
2975 while let Some(req) = builder_stream.try_next().await.unwrap() {
2976 match req {
2977 ftest::BuilderRequest::Build { runner, responder } => {
2978 drop(runner);
2979 responder.send(Ok("test://hippo")).unwrap();
2980 }
2981 }
2982 }
2983 });
2984
2985 let (realm_report_requests, realm_receive_requests) = mpsc::unbounded();
2986 let server_task = fasync::Task::spawn(async move {
2987 let _builder_task = builder_task;
2988 handle_realm_stream(realm_stream, realm_report_requests).await
2989 });
2990 let id: u64 = rand::random();
2991 let realm_name = format!("auto-{:x}", id);
2992 let component_realm_proxy =
2993 fclient::connect_to_protocol::<fcomponent::RealmMarker>().unwrap();
2994
2995 (
2996 RealmBuilder::build_struct(
2997 component_realm_proxy,
2998 realm_proxy,
2999 builder_proxy,
3000 crate::DEFAULT_COLLECTION_NAME.to_string(),
3001 false,
3002 realm_name,
3003 )
3004 .unwrap(),
3005 server_task,
3006 realm_receive_requests,
3007 )
3008 }
3009
3010 fn confirm_num_server_requests(
3013 mut server_requests: mpsc::UnboundedReceiver<ServerRequest>,
3014 num: usize,
3015 ) -> Vec<mpsc::UnboundedReceiver<ServerRequest>> {
3016 let mut discovered_receivers = vec![];
3017 for i in 0..num {
3018 match server_requests.next().now_or_never() {
3019 Some(Some(ServerRequest::AddChildRealm { receive_requests, .. })) => {
3020 discovered_receivers.push(receive_requests)
3021 }
3022 Some(Some(_)) => (),
3023 Some(None) => panic!("server_requests ended unexpectedly"),
3024 None => panic!("server_requests had less messages in it than we expected: {}", i),
3025 }
3026 }
3027 assert_matches!(server_requests.next().now_or_never(), None);
3028 discovered_receivers
3029 }
3030
3031 fn assert_add_child_realm(
3032 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3033 expected_name: &str,
3034 expected_options: ftest::ChildOptions,
3035 ) -> mpsc::UnboundedReceiver<ServerRequest> {
3036 match receive_server_requests.next().now_or_never() {
3037 Some(Some(ServerRequest::AddChildRealm { name, options, receive_requests }))
3038 if &name == expected_name && options == expected_options =>
3039 {
3040 receive_requests
3041 }
3042 req => panic!("match failed, received unexpected server request: {:?}", req),
3043 }
3044 }
3045
3046 fn assert_add_child_realm_from_relative_url(
3047 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3048 expected_name: &str,
3049 expected_relative_url: &str,
3050 expected_options: ftest::ChildOptions,
3051 ) -> mpsc::UnboundedReceiver<ServerRequest> {
3052 match receive_server_requests.next().now_or_never() {
3053 Some(Some(ServerRequest::AddChildRealmFromRelativeUrl {
3054 name,
3055 relative_url,
3056 options,
3057 receive_requests,
3058 })) if &name == expected_name
3059 && options == expected_options
3060 && relative_url == expected_relative_url =>
3061 {
3062 receive_requests
3063 }
3064 req => panic!("match failed, received unexpected server request: {:?}", req),
3065 }
3066 }
3067
3068 fn assert_add_child_realm_from_decl(
3069 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3070 expected_name: &str,
3071 expected_decl: &fdecl::Component,
3072 expected_options: ftest::ChildOptions,
3073 ) -> mpsc::UnboundedReceiver<ServerRequest> {
3074 match receive_server_requests.next().now_or_never() {
3075 Some(Some(ServerRequest::AddChildRealmFromDecl {
3076 name,
3077 decl,
3078 options,
3079 receive_requests,
3080 })) if &name == expected_name
3081 && options == expected_options
3082 && decl == *expected_decl =>
3083 {
3084 receive_requests
3085 }
3086 req => panic!("match failed, received unexpected server request: {:?}", req),
3087 }
3088 }
3089
3090 fn assert_read_only_directory(
3091 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3092 expected_directory_name: &str,
3093 expected_targets: Vec<impl Into<Ref>>,
3094 ) {
3095 let expected_targets = expected_targets
3096 .into_iter()
3097 .map(|t| {
3098 let t: Ref = t.into();
3099 t
3100 })
3101 .map(|t| {
3102 let t: fdecl::Ref = t.into();
3103 t
3104 })
3105 .collect::<Vec<_>>();
3106
3107 match receive_server_requests.next().now_or_never() {
3108 Some(Some(ServerRequest::ReadOnlyDirectory { name, to, .. }))
3109 if &name == expected_directory_name && to == expected_targets =>
3110 {
3111 return;
3112 }
3113 req => panic!("match failed, received unexpected server request: {:?}", req),
3114 }
3115 }
3116
3117 #[fuchsia::test]
3118 async fn add_child() {
3119 let (builder, _server_task, mut receive_server_requests) =
3120 new_realm_builder_and_server_task();
3121 let _child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3122 assert_matches!(
3123 receive_server_requests.next().await,
3124 Some(ServerRequest::AddChild { name, url, options })
3125 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3126 );
3127 assert_matches!(receive_server_requests.next().now_or_never(), None);
3128 }
3129
3130 #[fuchsia::test]
3131 async fn add_child_from_decl() {
3132 let (builder, _server_task, mut receive_server_requests) =
3133 new_realm_builder_and_server_task();
3134 let _child_a = builder
3135 .add_child_from_decl("a", cm_rust::ComponentDecl::default(), ChildOptions::new())
3136 .await
3137 .unwrap();
3138 assert_matches!(
3139 receive_server_requests.next().await,
3140 Some(ServerRequest::AddChildFromDecl { name, decl, options })
3141 if &name == "a"
3142 && decl == fdecl::Component::default()
3143 && options == ChildOptions::new().into()
3144 );
3145 assert_matches!(receive_server_requests.next().now_or_never(), None);
3146 }
3147
3148 #[fuchsia::test]
3149 async fn add_local_child() {
3150 let (builder, _server_task, mut receive_server_requests) =
3151 new_realm_builder_and_server_task();
3152 let _child_a = builder
3153 .add_local_child("a", |_| async move { Ok(()) }.boxed(), ChildOptions::new())
3154 .await
3155 .unwrap();
3156 assert_matches!(
3157 receive_server_requests.next().await,
3158 Some(ServerRequest::AddLocalChild { name, options })
3159 if &name == "a" && options == ChildOptions::new().into()
3160 );
3161 assert_matches!(receive_server_requests.next().now_or_never(), None);
3162 }
3163
3164 #[fuchsia::test]
3165 async fn add_child_realm() {
3166 let (builder, _server_task, mut receive_server_requests) =
3167 new_realm_builder_and_server_task();
3168 let child_realm_a = builder.add_child_realm("a", ChildOptions::new()).await.unwrap();
3169 let _child_b = child_realm_a.add_child("b", "test://b", ChildOptions::new()).await.unwrap();
3170 let child_realm_c = builder
3171 .add_child_realm_from_relative_url("c", "#c", ChildOptions::new())
3172 .await
3173 .unwrap();
3174 let _child_d = child_realm_c.add_child("d", "test://d", ChildOptions::new()).await.unwrap();
3175 let child_realm_e = builder
3176 .add_child_realm_from_decl("e", cm_rust::ComponentDecl::default(), ChildOptions::new())
3177 .await
3178 .unwrap();
3179 let _child_f = child_realm_e.add_child("f", "test://f", ChildOptions::new()).await.unwrap();
3180
3181 let mut receive_sub_realm_requests =
3182 assert_add_child_realm(&mut receive_server_requests, "a", ChildOptions::new().into());
3183 assert_matches!(
3184 receive_sub_realm_requests.next().await,
3185 Some(ServerRequest::AddChild { name, url, options })
3186 if &name == "b" && &url == "test://b" && options == ChildOptions::new().into()
3187 );
3188 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3189
3190 let mut receive_sub_realm_requests = assert_add_child_realm_from_relative_url(
3191 &mut receive_server_requests,
3192 "c",
3193 "#c",
3194 ChildOptions::new().into(),
3195 );
3196 assert_matches!(
3197 receive_sub_realm_requests.next().await,
3198 Some(ServerRequest::AddChild { name, url, options })
3199 if &name == "d" && &url == "test://d" && options == ChildOptions::new().into()
3200 );
3201 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3202
3203 let mut receive_sub_realm_requests = assert_add_child_realm_from_decl(
3204 &mut receive_server_requests,
3205 "e",
3206 &fdecl::Component::default(),
3207 ChildOptions::new().into(),
3208 );
3209 assert_matches!(
3210 receive_sub_realm_requests.next().await,
3211 Some(ServerRequest::AddChild { name, url, options })
3212 if &name == "f" && &url == "test://f" && options == ChildOptions::new().into()
3213 );
3214 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3215 assert_matches!(receive_server_requests.next().now_or_never(), None);
3216 }
3217
3218 #[fuchsia::test]
3219 async fn get_component_decl() {
3220 let (builder, _server_task, mut receive_server_requests) =
3221 new_realm_builder_and_server_task();
3222 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3223 let _decl = builder.get_component_decl(&child_a).await.unwrap();
3224
3225 assert_matches!(
3226 receive_server_requests.next().await,
3227 Some(ServerRequest::AddChild { name, url, options })
3228 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3229 );
3230 assert_matches!(
3231 receive_server_requests.next().await,
3232 Some(ServerRequest::GetComponentDecl { name }) if &name == "a"
3233 );
3234 assert_matches!(receive_server_requests.next().now_or_never(), None);
3235 }
3236
3237 #[fuchsia::test]
3238 async fn replace_component_decl() {
3239 let (builder, _server_task, mut receive_server_requests) =
3240 new_realm_builder_and_server_task();
3241 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3242 builder.replace_component_decl(&child_a, cm_rust::ComponentDecl::default()).await.unwrap();
3243
3244 assert_matches!(
3245 receive_server_requests.next().await,
3246 Some(ServerRequest::AddChild { name, url, options })
3247 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3248 );
3249 assert_matches!(
3250 receive_server_requests.next().await,
3251 Some(ServerRequest::ReplaceComponentDecl { name, component_decl })
3252 if &name == "a" && component_decl == fdecl::Component::default()
3253 );
3254 assert_matches!(receive_server_requests.next().now_or_never(), None);
3255 }
3256
3257 #[fuchsia::test]
3258 async fn get_realm_decl() {
3259 let (builder, _server_task, mut receive_server_requests) =
3260 new_realm_builder_and_server_task();
3261 let _decl = builder.get_realm_decl().await.unwrap();
3262
3263 assert_matches!(receive_server_requests.next().await, Some(ServerRequest::GetRealmDecl));
3264 assert_matches!(receive_server_requests.next().now_or_never(), None);
3265 }
3266
3267 #[fuchsia::test]
3268 async fn replace_realm_decl() {
3269 let (builder, _server_task, mut receive_server_requests) =
3270 new_realm_builder_and_server_task();
3271 builder.replace_realm_decl(cm_rust::ComponentDecl::default()).await.unwrap();
3272
3273 assert_matches!(
3274 receive_server_requests.next().await,
3275 Some(ServerRequest::ReplaceRealmDecl { component_decl })
3276 if component_decl == fdecl::Component::default()
3277 );
3278 assert_matches!(receive_server_requests.next().now_or_never(), None);
3279 }
3280
3281 #[fuchsia::test]
3282 async fn set_config_value() {
3283 let (builder, _server_task, mut receive_server_requests) =
3284 new_realm_builder_and_server_task();
3285 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3286 builder.init_mutable_config_from_package(&child_a).await.unwrap();
3287 builder.init_mutable_config_to_empty(&child_a).await.unwrap();
3288 builder.set_config_value(&child_a, "test_bool", false.into()).await.unwrap();
3289 builder.set_config_value(&child_a, "test_int16", (-2 as i16).into()).await.unwrap();
3290 builder.set_config_value(&child_a, "test_string", "test".to_string().into()).await.unwrap();
3291 builder
3292 .set_config_value(&child_a, "test_string_vector", vec!["hello", "fuchsia"].into())
3293 .await
3294 .unwrap();
3295
3296 assert_matches!(
3297 receive_server_requests.next().await,
3298 Some(ServerRequest::AddChild { name, url, options })
3299 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3300 );
3301
3302 assert_matches!(
3303 receive_server_requests.next().await,
3304 Some(ServerRequest::InitMutableConfigFromPackage { name }) if &name == "a"
3305 );
3306
3307 assert_matches!(
3308 receive_server_requests.next().await,
3309 Some(ServerRequest::InitMutableConfigToEmpty { name }) if &name == "a"
3310 );
3311
3312 assert_matches!(
3313 receive_server_requests.next().await,
3314 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3315 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(boolean))), ..
3316 }}) if &name == "a" && &key == "test_bool" && boolean == false
3317 );
3318
3319 assert_matches!(
3320 receive_server_requests.next().await,
3321 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3322 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Int16(int16))), ..
3323 }}) if &name == "a" && &key == "test_int16" && int16 == -2
3324 );
3325
3326 assert_matches!(
3327 receive_server_requests.next().await,
3328 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3329 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::String(string))), ..
3330 }}) if &name == "a" && &key == "test_string" && &string == "test"
3331 );
3332
3333 assert_matches!(
3334 receive_server_requests.next().await,
3335 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3336 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::StringVector(string_vector))), ..
3337 }}) if &name == "a" && &key == "test_string_vector" && string_vector == vec!["hello", "fuchsia"]
3338 );
3339
3340 assert_matches!(receive_server_requests.next().now_or_never(), None);
3341 }
3342
3343 #[fuchsia::test]
3344 async fn add_route() {
3345 let (builder, _server_task, mut receive_server_requests) =
3346 new_realm_builder_and_server_task();
3347 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3348 builder
3349 .add_route(
3350 Route::new()
3351 .capability(Capability::protocol_by_name("test"))
3352 .capability(Capability::directory("test2"))
3353 .capability(Capability::service_by_name("test3"))
3354 .capability(Capability::configuration("test4"))
3355 .capability(Capability::dictionary("test5"))
3356 .from(&child_a)
3357 .to(Ref::parent()),
3358 )
3359 .await
3360 .unwrap();
3361
3362 assert_matches!(
3363 receive_server_requests.next().await,
3364 Some(ServerRequest::AddChild { name, url, options })
3365 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3366 );
3367 assert_matches!(
3368 receive_server_requests.next().await,
3369 Some(ServerRequest::AddRoute { capabilities, from, to })
3370 if capabilities == vec![
3371 Capability::protocol_by_name("test").into(),
3372 Capability::directory("test2").into(),
3373 Capability::service_by_name("test3").into(),
3374 Capability::configuration("test4").into(),
3375 Capability::dictionary("test5").into(),
3376 ]
3377 && from == Ref::child("a").into()
3378 && to == vec![Ref::parent().into()]
3379 );
3380 assert_matches!(receive_server_requests.next().now_or_never(), None);
3381 }
3382
3383 #[fuchsia::test]
3384 async fn add_route_to_dictionary() {
3385 let (builder, _server_task, mut receive_server_requests) =
3386 new_realm_builder_and_server_task();
3387 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3388 builder
3389 .add_capability(cm_rust::CapabilityDecl::Dictionary(cm_rust::DictionaryDecl {
3390 name: "my_dict".parse().unwrap(),
3391 source_path: None,
3392 }))
3393 .await
3394 .unwrap();
3395 builder
3396 .add_route(
3397 Route::new()
3398 .capability(Capability::protocol_by_name("test"))
3399 .capability(Capability::directory("test2"))
3400 .capability(Capability::service_by_name("test3"))
3401 .capability(Capability::dictionary("test4"))
3402 .from(&child_a)
3403 .to(Ref::dictionary("self/my_dict")),
3404 )
3405 .await
3406 .unwrap();
3407
3408 assert_matches!(
3409 receive_server_requests.next().await,
3410 Some(ServerRequest::AddChild { name, url, options })
3411 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3412 );
3413 assert_matches!(
3414 receive_server_requests.next().await,
3415 Some(ServerRequest::AddCapability { .. })
3416 );
3417 assert_matches!(
3418 receive_server_requests.next().await,
3419 Some(ServerRequest::AddRoute { capabilities, from, to })
3420 if capabilities == vec![
3421 Capability::protocol_by_name("test").into(),
3422 Capability::directory("test2").into(),
3423 Capability::service_by_name("test3").into(),
3424 Capability::dictionary("test4").into(),
3425 ]
3426 && from == Ref::child("a").into()
3427 && to == vec![Ref::dictionary("self/my_dict").into()]
3428 );
3429 assert_matches!(receive_server_requests.next().now_or_never(), None);
3430 }
3431
3432 #[fuchsia::test]
3433 async fn add_route_from_dictionary() {
3434 let (builder, _server_task, mut receive_server_requests) =
3435 new_realm_builder_and_server_task();
3436 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3437 builder
3438 .add_route(
3439 Route::new()
3440 .capability(Capability::protocol_by_name("test"))
3441 .capability(Capability::directory("test2"))
3442 .capability(Capability::service_by_name("test3"))
3443 .capability(Capability::dictionary("test4"))
3444 .from(&child_a)
3445 .from_dictionary("source/dict")
3446 .to(Ref::parent()),
3447 )
3448 .await
3449 .unwrap();
3450
3451 assert_matches!(
3452 receive_server_requests.next().await,
3453 Some(ServerRequest::AddChild { name, url, options })
3454 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3455 );
3456
3457 let mut expected_capabilities = vec![];
3458 expected_capabilities.push({
3459 let mut c: ftest::Capability = Capability::protocol_by_name("test").into();
3460 if let ftest::Capability::Protocol(ref mut c) = c {
3461 c.from_dictionary = Some("source/dict".into());
3462 } else {
3463 unreachable!();
3464 }
3465 c
3466 });
3467 expected_capabilities.push({
3468 let mut c: ftest::Capability = Capability::directory("test2").into();
3469 if let ftest::Capability::Directory(ref mut c) = c {
3470 c.from_dictionary = Some("source/dict".into());
3471 } else {
3472 unreachable!();
3473 }
3474 c
3475 });
3476 expected_capabilities.push({
3477 let mut c: ftest::Capability = Capability::service_by_name("test3").into();
3478 if let ftest::Capability::Service(ref mut c) = c {
3479 c.from_dictionary = Some("source/dict".into());
3480 } else {
3481 unreachable!();
3482 }
3483 c
3484 });
3485 expected_capabilities.push({
3486 let mut c: ftest::Capability = Capability::dictionary("test4").into();
3487 if let ftest::Capability::Dictionary(ref mut c) = c {
3488 c.from_dictionary = Some("source/dict".into());
3489 } else {
3490 unreachable!();
3491 }
3492 c
3493 });
3494 assert_matches!(
3495 receive_server_requests.next().await,
3496 Some(ServerRequest::AddRoute { capabilities, from, to })
3497 if capabilities == expected_capabilities
3498 && from == Ref::child("a").into()
3499 && to == vec![Ref::parent().into()]
3500 );
3501 assert_matches!(receive_server_requests.next().now_or_never(), None);
3502 }
3503
3504 #[fuchsia::test]
3505 async fn add_child_to_sub_realm() {
3506 let (builder, _server_task, mut receive_server_requests) =
3507 new_realm_builder_and_server_task();
3508 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3509 let _child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3510 let mut receive_sub_realm_requests =
3511 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3512 assert_matches!(
3513 receive_sub_realm_requests.next().await,
3514 Some(ServerRequest::AddChild { name, url, options })
3515 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3516 );
3517 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3518 assert_matches!(receive_server_requests.next().now_or_never(), None);
3519 }
3520
3521 #[fuchsia::test]
3522 async fn add_child_from_decl_to_sub_realm() {
3523 let (builder, _server_task, mut receive_server_requests) =
3524 new_realm_builder_and_server_task();
3525 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3526 let _child_a = child_realm
3527 .add_child_from_decl("a", cm_rust::ComponentDecl::default(), ChildOptions::new())
3528 .await
3529 .unwrap();
3530 let mut receive_sub_realm_requests =
3531 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3532 assert_matches!(
3533 receive_sub_realm_requests.next().await,
3534 Some(ServerRequest::AddChildFromDecl { name, decl, options })
3535 if &name == "a"
3536 && decl == fdecl::Component::default()
3537 && options == ChildOptions::new().into()
3538 );
3539 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3540 assert_matches!(receive_server_requests.next().now_or_never(), None);
3541 }
3542
3543 #[fuchsia::test]
3544 async fn add_local_child_to_sub_realm() {
3545 let (builder, _server_task, mut receive_server_requests) =
3546 new_realm_builder_and_server_task();
3547 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3548 let _child_a = child_realm
3549 .add_local_child("a", |_| async move { Ok(()) }.boxed(), ChildOptions::new())
3550 .await
3551 .unwrap();
3552 let mut receive_sub_realm_requests =
3553 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3554 assert_matches!(
3555 receive_sub_realm_requests.next().await,
3556 Some(ServerRequest::AddLocalChild { name, options })
3557 if &name == "a" && options == ChildOptions::new().into()
3558 );
3559 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3560 assert_matches!(receive_server_requests.next().now_or_never(), None);
3561 }
3562
3563 #[fuchsia::test]
3564 async fn add_child_realm_to_child_realm() {
3565 let (builder, _server_task, mut receive_server_requests) =
3566 new_realm_builder_and_server_task();
3567 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3568 let child_realm_a = child_realm.add_child_realm("a", ChildOptions::new()).await.unwrap();
3569 let _child_b = child_realm_a.add_child("b", "test://b", ChildOptions::new()).await.unwrap();
3570
3571 let mut receive_sub_realm_requests =
3572 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3573 let mut receive_sub_sub_realm_requests = assert_add_child_realm(
3574 &mut receive_sub_realm_requests,
3575 "a",
3576 ChildOptions::new().into(),
3577 );
3578 assert_matches!(
3579 receive_sub_sub_realm_requests.next().await,
3580 Some(ServerRequest::AddChild { name, url, options })
3581 if &name == "b" && &url == "test://b" && options == ChildOptions::new().into()
3582 );
3583 assert_matches!(receive_sub_sub_realm_requests.next().now_or_never(), None);
3584 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3585 assert_matches!(receive_server_requests.next().now_or_never(), None);
3586 }
3587
3588 #[fuchsia::test]
3589 async fn get_component_decl_in_sub_realm() {
3590 let (builder, _server_task, mut receive_server_requests) =
3591 new_realm_builder_and_server_task();
3592 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3593 let child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3594 let _decl = child_realm.get_component_decl(&child_a).await.unwrap();
3595
3596 let mut receive_sub_realm_requests =
3597 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3598 assert_matches!(
3599 receive_sub_realm_requests.next().await,
3600 Some(ServerRequest::AddChild { name, url, options })
3601 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3602 );
3603 assert_matches!(
3604 receive_sub_realm_requests.next().await,
3605 Some(ServerRequest::GetComponentDecl { name }) if &name == "a"
3606 );
3607 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3608 assert_matches!(receive_server_requests.next().now_or_never(), None);
3609 }
3610
3611 #[fuchsia::test]
3612 async fn replace_component_decl_in_sub_realm() {
3613 let (builder, _server_task, mut receive_server_requests) =
3614 new_realm_builder_and_server_task();
3615 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3616 let child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3617 child_realm
3618 .replace_component_decl(&child_a, cm_rust::ComponentDecl::default())
3619 .await
3620 .unwrap();
3621
3622 let mut receive_sub_realm_requests =
3623 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3624 assert_matches!(
3625 receive_sub_realm_requests.next().await,
3626 Some(ServerRequest::AddChild { name, url, options })
3627 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3628 );
3629 assert_matches!(
3630 receive_sub_realm_requests.next().await,
3631 Some(ServerRequest::ReplaceComponentDecl { name, component_decl })
3632 if &name == "a" && component_decl == fdecl::Component::default()
3633 );
3634 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3635 assert_matches!(receive_server_requests.next().now_or_never(), None);
3636 }
3637
3638 #[fuchsia::test]
3639 async fn get_realm_decl_in_sub_realm() {
3640 let (builder, _server_task, mut receive_server_requests) =
3641 new_realm_builder_and_server_task();
3642 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3643 let _decl = child_realm.get_realm_decl().await.unwrap();
3644
3645 let mut receive_sub_realm_requests =
3646 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3647 assert_matches!(receive_sub_realm_requests.next().await, Some(ServerRequest::GetRealmDecl));
3648 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3649 assert_matches!(receive_server_requests.next().now_or_never(), None);
3650 }
3651
3652 #[fuchsia::test]
3653 async fn replace_realm_decl_in_sub_realm() {
3654 let (builder, _server_task, mut receive_server_requests) =
3655 new_realm_builder_and_server_task();
3656 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3657 child_realm.replace_realm_decl(cm_rust::ComponentDecl::default()).await.unwrap();
3658
3659 let mut receive_sub_realm_requests =
3660 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3661 assert_matches!(
3662 receive_sub_realm_requests.next().await,
3663 Some(ServerRequest::ReplaceRealmDecl { component_decl })
3664 if component_decl == fdecl::Component::default()
3665 );
3666 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3667 assert_matches!(receive_server_requests.next().now_or_never(), None);
3668 }
3669
3670 #[fuchsia::test]
3671 async fn add_route_in_sub_realm() {
3672 let (builder, _server_task, mut receive_server_requests) =
3673 new_realm_builder_and_server_task();
3674 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3675 let child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3676 child_realm
3677 .add_route(
3678 Route::new()
3679 .capability(Capability::protocol_by_name("test"))
3680 .capability(Capability::directory("test2"))
3681 .from(&child_a)
3682 .to(Ref::parent()),
3683 )
3684 .await
3685 .unwrap();
3686
3687 let mut receive_sub_realm_requests =
3688 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3689 assert_matches!(
3690 receive_sub_realm_requests.next().await,
3691 Some(ServerRequest::AddChild { name, url, options })
3692 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3693 );
3694 assert_matches!(
3695 receive_sub_realm_requests.next().await,
3696 Some(ServerRequest::AddRoute { capabilities, from, to })
3697 if capabilities == vec![
3698 Capability::protocol_by_name("test").into(),
3699 Capability::directory("test2").into(),
3700 ]
3701 && from == Ref::child("a").into()
3702 && to == vec![Ref::parent().into()]
3703 );
3704 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3705 assert_matches!(receive_server_requests.next().now_or_never(), None);
3706 }
3707
3708 #[fuchsia::test]
3709 async fn read_only_directory() {
3710 let (builder, _server_task, mut receive_server_requests) =
3711 new_realm_builder_and_server_task();
3712 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3713 builder
3714 .read_only_directory(
3715 "config",
3716 vec![&child_a],
3717 DirectoryContents::new().add_file("config.json", "{ \"hippos\": \"rule!\" }"),
3718 )
3719 .await
3720 .unwrap();
3721
3722 assert_matches!(
3723 receive_server_requests.next().await,
3724 Some(ServerRequest::AddChild { name, url, options })
3725 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3726 );
3727 assert_read_only_directory(&mut receive_server_requests, "config", vec![&child_a]);
3728 }
3729
3730 #[test]
3731 fn realm_builder_works_with_send() {
3732 let mut executor = fasync::SendExecutor::new(2);
3735 executor.run(async {
3736 let (builder, _server_task, _receive_server_requests) =
3737 new_realm_builder_and_server_task();
3738 let child_realm_a = builder.add_child_realm("a", ChildOptions::new()).await.unwrap();
3739 let child_b = builder
3740 .add_local_child("b", |_handles| pending().boxed(), ChildOptions::new())
3741 .await
3742 .unwrap();
3743 let child_c = builder.add_child("c", "test://c", ChildOptions::new()).await.unwrap();
3744 let child_e = builder
3745 .add_child_from_decl("e", cm_rust::ComponentDecl::default(), ChildOptions::new())
3746 .await
3747 .unwrap();
3748
3749 let decl_for_e = builder.get_component_decl(&child_e).await.unwrap();
3750 builder.replace_component_decl(&child_e, decl_for_e).await.unwrap();
3751 let realm_decl = builder.get_realm_decl().await.unwrap();
3752 builder.replace_realm_decl(realm_decl).await.unwrap();
3753 builder
3754 .add_route(
3755 Route::new()
3756 .capability(Capability::protocol::<fcomponent::RealmMarker>())
3757 .from(&child_e)
3758 .to(&child_c)
3759 .to(&child_b)
3760 .to(&child_realm_a)
3761 .to(Ref::parent()),
3762 )
3763 .await
3764 .unwrap();
3765 builder
3766 .read_only_directory(
3767 "config",
3768 vec![&child_e],
3769 DirectoryContents::new().add_file("config.json", "{ \"hippos\": \"rule!\" }"),
3770 )
3771 .await
3772 .unwrap();
3773 });
3774 }
3775
3776 #[fuchsia::test]
3777 async fn add_configurations() {
3778 let (builder, _server_task, mut receive_server_requests) =
3779 new_realm_builder_and_server_task();
3780 _ = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3781 _ = receive_server_requests.next().now_or_never();
3782
3783 builder
3784 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
3785 name: "my-config".to_string().fidl_into_native(),
3786 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(true)),
3787 }))
3788 .await
3789 .unwrap();
3790 match receive_server_requests.next().now_or_never() {
3791 Some(Some(ServerRequest::AddCapability { capability, .. })) => {
3792 let configuration = assert_matches!(capability, fdecl::Capability::Config(c) => c);
3793 assert_eq!(configuration.name, Some("my-config".to_string()));
3794 assert_eq!(
3795 configuration.value,
3796 Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true)))
3797 );
3798 }
3799 req => panic!("match failed, received unexpected server request: {:?}", req),
3800 };
3801 }
3802
3803 #[fuchsia::test]
3804 async fn add_environment_and_collection() {
3805 let (builder, _server_task, mut receive_server_requests) =
3806 new_realm_builder_and_server_task();
3807 _ = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3808 _ = receive_server_requests.next().now_or_never();
3809
3810 builder
3811 .add_environment(cm_rust::EnvironmentDecl {
3812 name: "driver-host-env".parse().unwrap(),
3813 extends: fdecl::EnvironmentExtends::Realm,
3814 runners: vec![],
3815 resolvers: vec![cm_rust::ResolverRegistration {
3816 resolver: "boot-resolver".parse().unwrap(),
3817 source: cm_rust::RegistrationSource::Child("fake-resolver".to_string()),
3818 scheme: "fuchsia-boot".to_string(),
3819 }],
3820 debug_capabilities: vec![],
3821 stop_timeout_ms: Some(20000),
3822 })
3823 .await
3824 .unwrap();
3825 match receive_server_requests.next().now_or_never() {
3826 Some(Some(ServerRequest::AddEnvironment { environment, .. })) => {
3827 assert_eq!(environment.name, Some("driver-host-env".to_string()));
3828 }
3829 req => panic!("match failed, received unexpected server request: {:?}", req),
3830 };
3831 builder
3832 .add_collection(cm_rust::CollectionDecl {
3833 name: "driver-hosts".parse().unwrap(),
3834 durability: fdecl::Durability::SingleRun,
3835 environment: Some("driver-host-env".parse().unwrap()),
3836 allowed_offers: Default::default(),
3837 allow_long_names: Default::default(),
3838 persistent_storage: None,
3839 })
3840 .await
3841 .unwrap();
3842 match receive_server_requests.next().now_or_never() {
3843 Some(Some(ServerRequest::AddCollection { collection, .. })) => {
3844 assert_eq!(collection.name, Some("driver-hosts".to_string()));
3845 }
3846 req => panic!("match failed, received unexpected server request: {:?}", req),
3847 };
3848 }
3849}