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