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 #[cfg(fuchsia_api_level_at_least = "26")]
1276 pub async fn add_child_realm_from_decl(
1277 &self,
1278 name: impl Into<String>,
1279 decl: cm_rust::ComponentDecl,
1280 options: ChildOptions,
1281 ) -> Result<SubRealmBuilder, Error> {
1282 self.root_realm.add_child_realm_from_decl(name, decl, options).await
1283 }
1284
1285 pub async fn add_local_child(
1287 &self,
1288 name: impl Into<String>,
1289 local_component_implementation: impl Fn(
1290 LocalComponentHandles,
1291 )
1292 -> BoxFuture<'static, Result<(), anyhow::Error>>
1293 + Sync
1294 + Send
1295 + 'static,
1296 options: ChildOptions,
1297 ) -> Result<ChildRef, Error> {
1298 self.root_realm.add_local_child(name, local_component_implementation, options).await
1299 }
1300
1301 pub async fn add_child(
1303 &self,
1304 name: impl Into<String>,
1305 url: impl Into<String>,
1306 options: ChildOptions,
1307 ) -> Result<ChildRef, Error> {
1308 self.root_realm.add_child(name, url, options).await
1309 }
1310
1311 pub async fn add_child_from_decl(
1313 &self,
1314 name: impl Into<String>,
1315 decl: cm_rust::ComponentDecl,
1316 options: ChildOptions,
1317 ) -> Result<ChildRef, Error> {
1318 self.root_realm.add_child_from_decl(name, decl, options).await
1319 }
1320
1321 pub async fn get_component_decl(
1332 &self,
1333 name: impl Into<ChildRef>,
1334 ) -> Result<cm_rust::ComponentDecl, Error> {
1335 self.root_realm.get_component_decl(name).await
1336 }
1337
1338 pub async fn replace_component_decl(
1349 &self,
1350 name: impl Into<ChildRef>,
1351 decl: cm_rust::ComponentDecl,
1352 ) -> Result<(), Error> {
1353 self.root_realm.replace_component_decl(name, decl).await
1354 }
1355
1356 pub async fn get_realm_decl(&self) -> Result<cm_rust::ComponentDecl, Error> {
1358 self.root_realm.get_realm_decl().await
1359 }
1360
1361 pub async fn replace_realm_decl(&self, decl: cm_rust::ComponentDecl) -> Result<(), Error> {
1363 self.root_realm.replace_realm_decl(decl).await
1364 }
1365
1366 pub async fn add_route(&self, route: Route) -> Result<(), Error> {
1368 self.root_realm.add_route(route).await
1369 }
1370
1371 pub async fn init_mutable_config_from_package(
1373 &self,
1374 name: impl Into<ChildRef>,
1375 ) -> Result<(), Error> {
1376 self.root_realm.init_mutable_config_from_package(name).await
1377 }
1378
1379 pub async fn init_mutable_config_to_empty(
1381 &self,
1382 name: impl Into<ChildRef>,
1383 ) -> Result<(), Error> {
1384 self.root_realm.init_mutable_config_to_empty(name).await
1385 }
1386
1387 pub async fn set_config_value(
1389 &self,
1390 name: impl Into<ChildRef>,
1391 key: &str,
1392 value: cm_rust::ConfigValue,
1393 ) -> Result<(), Error> {
1394 self.root_realm.set_config_value(name, key, value).await
1395 }
1396
1397 pub async fn read_only_directory(
1401 &self,
1402 directory_name: impl Into<String>,
1403 to: Vec<impl Into<Ref>>,
1404 directory_contents: DirectoryContents,
1405 ) -> Result<(), Error> {
1406 self.root_realm.read_only_directory(directory_name, to, directory_contents).await
1407 }
1408
1409 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1413 pub async fn add_storage(
1414 &self,
1415 storage_name: impl Into<String>,
1416 to: Vec<impl Into<Ref>>,
1417 storage_admin: Option<ServerEnd<fcomponent::StorageAdminMarker>>,
1418 ) -> Result<(), Error> {
1419 self.root_realm.add_storage(storage_name, to, storage_admin).await
1420 }
1421
1422 pub async fn add_capability(&self, capability: cm_rust::CapabilityDecl) -> Result<(), Error> {
1424 self.root_realm.add_capability(capability).await
1425 }
1426
1427 pub async fn add_collection(
1429 &self,
1430 collection: cm_rust::CollectionDecl,
1431 ) -> Result<CollectionRef, Error> {
1432 self.root_realm.add_collection(collection).await
1433 }
1434
1435 pub async fn add_environment(
1437 &self,
1438 environment: cm_rust::EnvironmentDecl,
1439 ) -> Result<(), Error> {
1440 self.root_realm.add_environment(environment).await
1441 }
1442}
1443
1444#[derive(Debug, Clone)]
1445pub struct SubRealmBuilder {
1446 realm_proxy: ftest::RealmProxy,
1447 realm_path: Vec<String>,
1448 local_component_runner_builder: LocalComponentRunnerBuilder,
1449 name: String,
1450}
1451
1452impl SubRealmBuilder {
1453 pub async fn add_child_realm(
1454 &self,
1455 name: impl Into<String>,
1456 options: ChildOptions,
1457 ) -> Result<Self, Error> {
1458 let name: String = name.into();
1459 let (child_realm_proxy, child_realm_server_end) = create_proxy::<ftest::RealmMarker>();
1460 self.realm_proxy.add_child_realm(&name, &options.into(), child_realm_server_end).await??;
1461
1462 let mut child_path = self.realm_path.clone();
1463 child_path.push(name.clone());
1464 Ok(SubRealmBuilder {
1465 realm_proxy: child_realm_proxy,
1466 realm_path: child_path,
1467 local_component_runner_builder: self.local_component_runner_builder.clone(),
1468 name,
1469 })
1470 }
1471
1472 pub async fn add_child_realm_from_relative_url(
1473 &self,
1474 name: impl Into<String>,
1475 relative_url: impl Into<String>,
1476 options: ChildOptions,
1477 ) -> Result<SubRealmBuilder, Error> {
1478 let name: String = name.into();
1479 let (child_realm_proxy, child_realm_server_end) = create_proxy::<ftest::RealmMarker>();
1480 self.realm_proxy
1481 .add_child_realm_from_relative_url(
1482 &name,
1483 &relative_url.into(),
1484 &options.into(),
1485 child_realm_server_end,
1486 )
1487 .await??;
1488
1489 let mut child_path = self.realm_path.clone();
1490 child_path.push(name.clone());
1491 Ok(SubRealmBuilder {
1492 realm_proxy: child_realm_proxy,
1493 realm_path: child_path,
1494 local_component_runner_builder: self.local_component_runner_builder.clone(),
1495 name,
1496 })
1497 }
1498
1499 #[cfg(fuchsia_api_level_at_least = "26")]
1500 pub async fn add_child_realm_from_decl(
1501 &self,
1502 name: impl Into<String>,
1503 decl: cm_rust::ComponentDecl,
1504 options: ChildOptions,
1505 ) -> Result<SubRealmBuilder, Error> {
1506 let name: String = name.into();
1507 let (child_realm_proxy, child_realm_server_end) = create_proxy::<ftest::RealmMarker>();
1508 self.realm_proxy
1509 .add_child_realm_from_decl(
1510 &name,
1511 &decl.native_into_fidl(),
1512 &options.into(),
1513 child_realm_server_end,
1514 )
1515 .await??;
1516
1517 let mut child_path = self.realm_path.clone();
1518 child_path.push(name.clone());
1519 Ok(SubRealmBuilder {
1520 realm_proxy: child_realm_proxy,
1521 realm_path: child_path,
1522 local_component_runner_builder: self.local_component_runner_builder.clone(),
1523 name,
1524 })
1525 }
1526
1527 pub async fn add_local_child<M>(
1529 &self,
1530 name: impl Into<String>,
1531 local_component_implementation: M,
1532 options: ChildOptions,
1533 ) -> Result<ChildRef, Error>
1534 where
1535 M: Fn(LocalComponentHandles) -> BoxFuture<'static, Result<(), anyhow::Error>>
1536 + Sync
1537 + Send
1538 + 'static,
1539 {
1540 let name: String = name.into();
1541 self.realm_proxy.add_local_child(&name, &options.into()).await??;
1542
1543 let mut child_path = self.realm_path.clone();
1544 child_path.push(name.clone());
1545 self.local_component_runner_builder
1546 .register_local_component(child_path.join("/"), local_component_implementation)
1547 .await?;
1548
1549 Ok(ChildRef::new(name, self.realm_path.clone()))
1550 }
1551
1552 pub async fn add_child(
1554 &self,
1555 name: impl Into<String>,
1556 url: impl Into<String>,
1557 options: ChildOptions,
1558 ) -> Result<ChildRef, Error> {
1559 let name: String = name.into();
1560 self.realm_proxy.add_child(&name, &url.into(), &options.into()).await??;
1561 Ok(ChildRef::new(name, self.realm_path.clone()))
1562 }
1563
1564 pub async fn add_child_from_decl(
1566 &self,
1567 name: impl Into<String>,
1568 decl: cm_rust::ComponentDecl,
1569 options: ChildOptions,
1570 ) -> Result<ChildRef, Error> {
1571 let name: String = name.into();
1572 self.realm_proxy
1573 .add_child_from_decl(&name, &decl.native_into_fidl(), &options.into())
1574 .await??;
1575 Ok(ChildRef::new(name, self.realm_path.clone()))
1576 }
1577
1578 pub async fn get_component_decl(
1580 &self,
1581 child_ref: impl Into<ChildRef>,
1582 ) -> Result<cm_rust::ComponentDecl, Error> {
1583 let child_ref: ChildRef = child_ref.into();
1584 child_ref.check_scope(&self.realm_path)?;
1585 let decl = self.realm_proxy.get_component_decl(&child_ref.name).await??;
1586 Ok(decl.fidl_into_native())
1587 }
1588
1589 pub async fn replace_component_decl(
1591 &self,
1592 child_ref: impl Into<ChildRef>,
1593 decl: cm_rust::ComponentDecl,
1594 ) -> Result<(), Error> {
1595 let child_ref: ChildRef = child_ref.into();
1596 child_ref.check_scope(&self.realm_path)?;
1597 self.realm_proxy
1598 .replace_component_decl(&child_ref.name, &decl.native_into_fidl())
1599 .await??;
1600 Ok(())
1601 }
1602
1603 pub async fn get_realm_decl(&self) -> Result<cm_rust::ComponentDecl, Error> {
1605 Ok(self.realm_proxy.get_realm_decl().await??.fidl_into_native())
1606 }
1607
1608 pub async fn replace_realm_decl(&self, decl: cm_rust::ComponentDecl) -> Result<(), Error> {
1610 self.realm_proxy.replace_realm_decl(&decl.native_into_fidl()).await?.map_err(Into::into)
1611 }
1612
1613 pub async fn init_mutable_config_from_package(
1615 &self,
1616 child_ref: impl Into<ChildRef>,
1617 ) -> Result<(), Error> {
1618 let child_ref = child_ref.into();
1619 child_ref.check_scope(&self.realm_path)?;
1620 self.realm_proxy
1621 .init_mutable_config_from_package(&child_ref.name)
1622 .await?
1623 .map_err(Into::into)
1624 }
1625
1626 pub async fn init_mutable_config_to_empty(
1628 &self,
1629 child_ref: impl Into<ChildRef>,
1630 ) -> Result<(), Error> {
1631 let child_ref = child_ref.into();
1632 child_ref.check_scope(&self.realm_path)?;
1633 self.realm_proxy.init_mutable_config_to_empty(&child_ref.name).await?.map_err(Into::into)
1634 }
1635
1636 pub async fn set_config_value(
1638 &self,
1639 child_ref: impl Into<ChildRef>,
1640 key: &str,
1641 value: cm_rust::ConfigValue,
1642 ) -> Result<(), Error> {
1643 let child_ref: ChildRef = child_ref.into();
1644 child_ref.check_scope(&self.realm_path)?;
1645 self.realm_proxy
1646 .set_config_value(
1647 &child_ref.name,
1648 key,
1649 &cm_rust::ConfigValueSpec { value }.native_into_fidl(),
1650 )
1651 .await?
1652 .map_err(Into::into)
1653 }
1654
1655 pub async fn add_route(&self, route: Route) -> Result<(), Error> {
1657 let from = route.from.ok_or(Error::MissingSource)?;
1658
1659 #[allow(unused_mut)] let mut capabilities = route.capabilities;
1661 for target in &route.to {
1662 target.check_scope(&self.realm_path)?;
1663 }
1664 from.check_scope(&self.realm_path)?;
1665 let (route_source, from_dictionary) = from.into_fidl(RefContext::Source);
1666 if !capabilities.is_empty() {
1667 let route_targets = route
1668 .to
1669 .into_iter()
1670 .map(|to| {
1671 let (to, path) = to.into_fidl(RefContext::Target);
1672 assert_matches!(path, None);
1673 to
1674 })
1675 .collect::<Vec<fdecl::Ref>>();
1676 #[cfg(fuchsia_api_level_at_least = "25")]
1681 let fut = self.realm_proxy.add_route_from_dictionary(
1682 &capabilities,
1683 &route_source,
1684 from_dictionary.as_ref().map(|s| s.as_str()).unwrap_or("."),
1685 &route_targets,
1686 );
1687 #[cfg(not(fuchsia_api_level_at_least = "25"))]
1688 let fut = self.realm_proxy.add_route(&capabilities, &route_source, &route_targets);
1689 fut.await??;
1690 }
1691 Ok(())
1692 }
1693
1694 pub async fn read_only_directory(
1698 &self,
1699 directory_name: impl Into<String>,
1700 to: Vec<impl Into<Ref>>,
1701 directory_contents: DirectoryContents,
1702 ) -> Result<(), Error> {
1703 let to: Vec<Ref> = to.into_iter().map(|t| t.into()).collect();
1704 for target in &to {
1705 target.check_scope(&self.realm_path)?;
1706 }
1707 let to = to
1708 .into_iter()
1709 .map(|to| {
1710 let (to, path) = to.into_fidl(RefContext::Target);
1711 assert_matches!(path, None);
1712 to
1713 })
1714 .collect::<Vec<_>>();
1715
1716 let fut = self.realm_proxy.read_only_directory(
1717 &directory_name.into(),
1718 &to,
1719 directory_contents.into(),
1720 );
1721 fut.await??;
1722 Ok(())
1723 }
1724
1725 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1726 pub async fn add_storage(
1727 &self,
1728 storage_name: impl Into<String>,
1729 to: Vec<impl Into<Ref>>,
1730 storage_admin: Option<ServerEnd<fcomponent::StorageAdminMarker>>,
1731 ) -> Result<(), Error> {
1732 let to: Vec<Ref> = to.into_iter().map(Into::into).collect();
1733 for target in &to {
1734 target.check_scope(&self.realm_path)?;
1735 }
1736 let to = to
1737 .into_iter()
1738 .map(|to| {
1739 let (to, path) = to.into_fidl(RefContext::Target);
1740 assert_matches!(path, None);
1741 to
1742 })
1743 .collect::<Vec<_>>();
1744
1745 let fut = self.realm_proxy.add_storage(&storage_name.into(), &to, storage_admin);
1746 fut.await??;
1747 Ok(())
1748 }
1749
1750 pub async fn add_capability(&self, capability: cm_rust::CapabilityDecl) -> Result<(), Error> {
1752 self.realm_proxy.add_capability(&capability.native_into_fidl()).await??;
1753 Ok(())
1754 }
1755
1756 pub async fn add_collection(
1758 &self,
1759 collection: cm_rust::CollectionDecl,
1760 ) -> Result<CollectionRef, Error> {
1761 let name = collection.name.clone().into();
1762 self.realm_proxy.add_collection(&collection.native_into_fidl()).await??;
1763 Ok(CollectionRef::new(name, self.realm_path.clone()))
1764 }
1765
1766 pub async fn add_environment(
1768 &self,
1769 environment: cm_rust::EnvironmentDecl,
1770 ) -> Result<(), Error> {
1771 self.realm_proxy.add_environment(&environment.native_into_fidl()).await??;
1772 Ok(())
1773 }
1774
1775 pub async fn with_nested_component_manager(
1785 &self,
1786 component_manager_fragment_only_url: &str,
1787 ) -> Result<(), Error> {
1788 self.realm_proxy
1789 .use_nested_component_manager(component_manager_fragment_only_url)
1790 .await?
1791 .map_err(Into::into)
1792 }
1793}
1794
1795impl Display for SubRealmBuilder {
1796 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1797 write!(f, "#{}", self.name)
1798 }
1799}
1800
1801pub struct DirectoryContents {
1804 contents: HashMap<String, fmem::Buffer>,
1805}
1806
1807impl DirectoryContents {
1808 pub fn new() -> Self {
1809 Self { contents: HashMap::new() }
1810 }
1811
1812 pub fn add_file(mut self, path: impl Into<String>, contents: impl Into<Vec<u8>>) -> Self {
1813 let contents: Vec<u8> = contents.into();
1814 let vmo = zx::Vmo::create(4096).expect("failed to create a VMO");
1815 vmo.write(&contents, 0).expect("failed to write to VMO");
1816 let buffer = fmem::Buffer { vmo, size: contents.len() as u64 };
1817 self.contents.insert(path.into(), buffer);
1818 self
1819 }
1820}
1821
1822impl Clone for DirectoryContents {
1823 fn clone(&self) -> Self {
1824 let mut new_self = Self::new();
1825 for (path, buf) in self.contents.iter() {
1826 new_self.contents.insert(
1827 path.clone(),
1828 fmem::Buffer {
1829 vmo: buf
1830 .vmo
1831 .create_child(zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE, 0, buf.size)
1832 .expect("failed to clone VMO"),
1833 size: buf.size,
1834 },
1835 );
1836 }
1837 new_self
1838 }
1839}
1840
1841impl From<DirectoryContents> for ftest::DirectoryContents {
1842 fn from(input: DirectoryContents) -> ftest::DirectoryContents {
1843 ftest::DirectoryContents {
1844 entries: input
1845 .contents
1846 .into_iter()
1847 .map(|(path, buf)| ftest::DirectoryEntry { file_path: path, file_contents: buf })
1848 .collect(),
1849 }
1850 }
1851}
1852
1853#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1856pub struct EventStream {
1857 name: String,
1858 scope: Option<Vec<Ref>>,
1859 rename: Option<String>,
1860 path: Option<String>,
1861}
1862
1863impl EventStream {
1864 pub fn new(name: impl Into<String>) -> Self {
1866 Self { name: name.into(), scope: None, rename: None, path: None }
1867 }
1868
1869 pub fn with_scope(mut self, scope: impl Into<Ref>) -> Self {
1872 self.scope.get_or_insert(vec![]).push(scope.into());
1873 self
1874 }
1875
1876 pub fn path(mut self, path: impl Into<String>) -> Self {
1881 self.path = Some(path.into());
1882 self
1883 }
1884
1885 pub fn as_(mut self, name: impl Into<String>) -> Self {
1887 self.rename = Some(name.into());
1888 self
1889 }
1890}
1891
1892#[derive(Debug, Clone)]
1894pub struct ChildOptions {
1895 startup: fdecl::StartupMode,
1896 environment: Option<String>,
1897 on_terminate: fdecl::OnTerminate,
1898 config_overrides: Option<Vec<fdecl::ConfigOverride>>,
1899}
1900
1901impl ChildOptions {
1902 pub fn new() -> Self {
1903 Self {
1904 startup: fdecl::StartupMode::Lazy,
1905 environment: None,
1906 on_terminate: fdecl::OnTerminate::None,
1907 config_overrides: None,
1908 }
1909 }
1910
1911 pub fn eager(mut self) -> Self {
1912 self.startup = fdecl::StartupMode::Eager;
1913 self
1914 }
1915
1916 pub fn environment(mut self, environment: impl Into<String>) -> Self {
1917 self.environment = Some(environment.into());
1918 self
1919 }
1920
1921 pub fn reboot_on_terminate(mut self) -> Self {
1922 self.on_terminate = fdecl::OnTerminate::Reboot;
1923 self
1924 }
1925
1926 pub fn config_overrides(
1927 mut self,
1928 config_overrides: impl Into<Vec<fdecl::ConfigOverride>>,
1929 ) -> Self {
1930 self.config_overrides = Some(config_overrides.into());
1931 self
1932 }
1933}
1934
1935impl Into<ftest::ChildOptions> for ChildOptions {
1936 fn into(self) -> ftest::ChildOptions {
1937 ftest::ChildOptions {
1938 startup: Some(self.startup),
1939 environment: self.environment,
1940 on_terminate: Some(self.on_terminate),
1941 config_overrides: self.config_overrides,
1942 ..Default::default()
1943 }
1944 }
1945}
1946
1947pub struct ScopedInstanceFactory {
1949 realm_proxy: Option<fcomponent::RealmProxy>,
1950 collection_name: String,
1951}
1952
1953impl ScopedInstanceFactory {
1954 pub fn new(collection_name: impl Into<String>) -> Self {
1956 ScopedInstanceFactory { realm_proxy: None, collection_name: collection_name.into() }
1957 }
1958
1959 pub fn with_realm_proxy(mut self, realm_proxy: fcomponent::RealmProxy) -> Self {
1963 self.realm_proxy = Some(realm_proxy);
1964 self
1965 }
1966
1967 pub async fn new_instance(
1970 &self,
1971 url: impl Into<String>,
1972 ) -> Result<ScopedInstance, anyhow::Error> {
1973 let id: u64 = rand::random();
1974 let child_name = format!("auto-{:x}", id);
1975 self.new_named_instance(child_name, url).await
1976 }
1977
1978 pub async fn new_named_instance(
1988 &self,
1989 child_name: impl Into<String>,
1990 url: impl Into<String>,
1991 ) -> Result<ScopedInstance, anyhow::Error> {
1992 let realm = if let Some(realm_proxy) = self.realm_proxy.as_ref() {
1993 realm_proxy.clone()
1994 } else {
1995 fclient::realm().context("Failed to connect to Realm service")?
1996 };
1997 let child_name = child_name.into();
1998 let collection_ref = fdecl::CollectionRef { name: self.collection_name.clone() };
1999 let child_decl = fdecl::Child {
2000 name: Some(child_name.clone()),
2001 url: Some(url.into()),
2002 startup: Some(fdecl::StartupMode::Lazy),
2003 ..Default::default()
2004 };
2005 let (controller_proxy, controller) = create_proxy::<fcomponent::ControllerMarker>();
2006 let child_args = fcomponent::CreateChildArgs {
2007 numbered_handles: None,
2008 controller: Some(controller),
2009 ..Default::default()
2010 };
2011 let () = realm
2012 .create_child(&collection_ref, &child_decl, child_args)
2013 .await
2014 .context("CreateChild FIDL failed.")?
2015 .map_err(|e| format_err!("Failed to create child: {:?}", e))?;
2016 let child_ref = fdecl::ChildRef {
2017 name: child_name.clone(),
2018 collection: Some(self.collection_name.clone()),
2019 };
2020 let (exposed_dir, server) = endpoints::create_proxy::<fio::DirectoryMarker>();
2021 let () = realm
2022 .open_exposed_dir(&child_ref, server)
2023 .await
2024 .context("OpenExposedDir FIDL failed.")?
2025 .map_err(|e|
2026 format_err!("Failed to open exposed dir of child: {:?}", e))?;
2030 Ok(ScopedInstance {
2031 realm,
2032 child_name,
2033 collection: self.collection_name.clone(),
2034 exposed_dir,
2035 destroy_channel: None,
2036 controller_proxy,
2037 })
2038 }
2039}
2040
2041#[must_use = "Dropping `ScopedInstance` will cause the component instance to be stopped and destroyed."]
2044pub struct ScopedInstance {
2045 realm: fcomponent::RealmProxy,
2046 child_name: String,
2047 collection: String,
2048 exposed_dir: fio::DirectoryProxy,
2049 destroy_channel: Option<
2050 futures::channel::oneshot::Sender<
2051 Result<
2052 fidl::client::QueryResponseFut<fcomponent::RealmDestroyChildResult>,
2053 anyhow::Error,
2054 >,
2055 >,
2056 >,
2057 controller_proxy: fcomponent::ControllerProxy,
2058}
2059
2060impl ScopedInstance {
2061 pub async fn new(coll: String, url: String) -> Result<Self, anyhow::Error> {
2064 ScopedInstanceFactory::new(coll).new_instance(url).await
2065 }
2066
2067 pub async fn new_with_name(
2073 child_name: String,
2074 collection: String,
2075 url: String,
2076 ) -> Result<Self, anyhow::Error> {
2077 ScopedInstanceFactory::new(collection).new_named_instance(child_name, url).await
2078 }
2079
2080 pub async fn is_started(&self) -> Result<bool, anyhow::Error> {
2082 Ok(self
2083 .controller_proxy
2084 .is_started()
2085 .await
2086 .context("failed to use controller proxy")?
2087 .map_err(|e| format_err!("failed to determine if component is started: {:?}", e))?)
2088 }
2089
2090 pub async fn start(&self) -> Result<ExecutionController, anyhow::Error> {
2092 self.start_with_args(fcomponent::StartChildArgs::default()).await
2093 }
2094
2095 pub async fn start_with_args(
2098 &self,
2099 args: fcomponent::StartChildArgs,
2100 ) -> Result<ExecutionController, anyhow::Error> {
2101 let (execution_proxy, execution_server_end) =
2102 create_proxy::<fcomponent::ExecutionControllerMarker>();
2103 self.controller_proxy
2104 .start(args, execution_server_end)
2105 .await
2106 .context("failed to use controller proxy")?
2107 .map_err(|e| format_err!("failed to start component: {:?}", e))?;
2108 Ok(ExecutionController::new(execution_proxy))
2109 }
2110
2111 pub fn controller(&self) -> &fcomponent::ControllerProxy {
2112 &self.controller_proxy
2113 }
2114
2115 pub fn connect_to_binder(&self) -> Result<fcomponent::BinderProxy, anyhow::Error> {
2120 let binder: fcomponent::BinderProxy = self
2121 .connect_to_protocol_at_exposed_dir()
2122 .context("failed to connect to fuchsia.component.Binder")?;
2123
2124 Ok(binder)
2125 }
2126
2127 pub async fn start_with_binder_sync(&self) -> Result<(), anyhow::Error> {
2135 let mut event_stream = component_events::events::EventStream::open()
2136 .await
2137 .context("failed to create EventSource")?;
2138
2139 let _: ClientEnd<fcomponent::BinderMarker> = self
2140 .connect_to_protocol_at_exposed_dir()
2141 .context("failed to connect to fuchsia.component.Binder")?;
2142
2143 let _ = EventMatcher::ok()
2144 .moniker(self.moniker())
2145 .wait::<Started>(&mut event_stream)
2146 .await
2147 .context("failed to observe Started event")?;
2148
2149 Ok(())
2150 }
2151
2152 pub fn connect_to_protocol_at_exposed_dir<T: fclient::Connect>(
2154 &self,
2155 ) -> Result<T, anyhow::Error> {
2156 T::connect_at_dir_root(&self.exposed_dir)
2157 }
2158
2159 pub fn connect_to_named_protocol_at_exposed_dir<P: DiscoverableProtocolMarker>(
2161 &self,
2162 protocol_name: &str,
2163 ) -> Result<P::Proxy, anyhow::Error> {
2164 fclient::connect_to_named_protocol_at_dir_root::<P>(&self.exposed_dir, protocol_name)
2165 }
2166
2167 pub fn connect_request_to_protocol_at_exposed_dir<P: DiscoverableProtocolMarker>(
2170 &self,
2171 server_end: ServerEnd<P>,
2172 ) -> Result<(), anyhow::Error> {
2173 self.connect_request_to_named_protocol_at_exposed_dir(
2174 P::PROTOCOL_NAME,
2175 server_end.into_channel(),
2176 )
2177 }
2178
2179 pub fn connect_request_to_named_protocol_at_exposed_dir(
2182 &self,
2183 protocol_name: &str,
2184 server_end: zx::Channel,
2185 ) -> Result<(), anyhow::Error> {
2186 self.exposed_dir
2187 .open(protocol_name, fio::Flags::PROTOCOL_SERVICE, &Default::default(), server_end)
2188 .map_err(Into::into)
2189 }
2190
2191 pub fn get_exposed_dir(&self) -> &fio::DirectoryProxy {
2193 &self.exposed_dir
2194 }
2195
2196 pub fn destroy_waiter_taken(&self) -> bool {
2198 self.destroy_channel.is_some()
2199 }
2200
2201 pub fn take_destroy_waiter(
2204 &mut self,
2205 ) -> impl futures::Future<Output = Result<(), anyhow::Error>> {
2206 if self.destroy_channel.is_some() {
2207 panic!("destroy waiter already taken");
2208 }
2209 let (sender, receiver) = futures::channel::oneshot::channel();
2210 self.destroy_channel = Some(sender);
2211 receiver.err_into().and_then(futures::future::ready).and_then(
2212 |fidl_fut: fidl::client::QueryResponseFut<_>| {
2213 fidl_fut.map(|r: Result<Result<(), fidl_fuchsia_component::Error>, fidl::Error>| {
2214 r.context("DestroyChild FIDL error")?
2215 .map_err(|e| format_err!("Failed to destroy child: {:?}", e))
2216 })
2217 },
2218 )
2219 }
2220
2221 pub fn child_name(&self) -> &str {
2223 self.child_name.as_str()
2224 }
2225
2226 pub fn moniker(&self) -> String {
2228 format!("./{}:{}", self.collection, self.child_name)
2229 }
2230}
2231
2232impl Drop for ScopedInstance {
2233 fn drop(&mut self) {
2234 let Self { realm, collection, child_name, destroy_channel, .. } = self;
2235 let child_ref =
2236 fdecl::ChildRef { name: child_name.clone(), collection: Some(collection.clone()) };
2237 let result = Ok(realm.destroy_child(&child_ref));
2243 if let Some(chan) = destroy_channel.take() {
2244 let () = chan.send(result).unwrap_or_else(|result| {
2245 warn!("Failed to send result for destroyed scoped instance. Result={:?}", result);
2246 });
2247 }
2248 }
2249}
2250
2251pub struct ExecutionController {
2256 execution_proxy: fcomponent::ExecutionControllerProxy,
2257 execution_event_stream: fcomponent::ExecutionControllerEventStream,
2258}
2259
2260impl ExecutionController {
2261 fn new(execution_proxy: fcomponent::ExecutionControllerProxy) -> Self {
2262 let execution_event_stream = execution_proxy.take_event_stream();
2263 Self { execution_proxy, execution_event_stream }
2264 }
2265
2266 pub async fn stop(self) -> Result<fcomponent::StoppedPayload, anyhow::Error> {
2269 let _ = self.execution_proxy.stop();
2273 self.wait_for_stop().await
2274 }
2275
2276 pub async fn wait_for_stop(mut self) -> Result<fcomponent::StoppedPayload, anyhow::Error> {
2278 loop {
2279 match self.execution_event_stream.try_next().await {
2280 Ok(Some(fcomponent::ExecutionControllerEvent::OnStop { stopped_payload })) => {
2281 return Ok(stopped_payload);
2282 }
2283 Ok(Some(fcomponent::ExecutionControllerEvent::_UnknownEvent {
2284 ordinal, ..
2285 })) => {
2286 warn!(ordinal:%; "fuchsia.component/ExecutionController delivered unknown event");
2287 }
2288 Ok(None) => {
2289 return Err(format_err!("ExecutionController closed and no OnStop received"));
2290 }
2291 Err(e) => {
2292 return Err(format_err!("failed to wait for OnStop: {:?}", e));
2293 }
2294 }
2295 }
2296 }
2297}
2298
2299#[cfg(test)]
2300mod tests {
2301 use super::*;
2302 use assert_matches::assert_matches;
2303 use fidl::endpoints::create_proxy_and_stream;
2304 use fidl_fuchsia_component as fcomponent;
2305 use futures::channel::mpsc;
2306 use futures::future::pending;
2307 use futures::{SinkExt, StreamExt};
2308
2309 #[fuchsia::test]
2314 fn child_options_to_fidl() {
2315 let options: ftest::ChildOptions = ChildOptions::new().into();
2316 assert_eq!(
2317 options,
2318 ftest::ChildOptions {
2319 startup: Some(fdecl::StartupMode::Lazy),
2321 on_terminate: Some(fdecl::OnTerminate::None),
2322 ..Default::default()
2323 },
2324 );
2325 assert_eq!(
2326 options,
2327 ftest::ChildOptions {
2328 startup: Some(fdecl::StartupMode::Lazy),
2329 environment: None,
2330 on_terminate: Some(fdecl::OnTerminate::None),
2331 config_overrides: None,
2332 __source_breaking: fidl::marker::SourceBreaking,
2333 },
2334 );
2335 let options: ftest::ChildOptions = ChildOptions::new().eager().into();
2336 assert_eq!(
2337 options,
2338 ftest::ChildOptions {
2339 startup: Some(fdecl::StartupMode::Eager),
2340 environment: None,
2341 on_terminate: Some(fdecl::OnTerminate::None),
2342 config_overrides: None,
2343 __source_breaking: fidl::marker::SourceBreaking,
2344 },
2345 );
2346 let options: ftest::ChildOptions = ChildOptions::new().environment("test_env").into();
2347 assert_eq!(
2348 options,
2349 ftest::ChildOptions {
2350 startup: Some(fdecl::StartupMode::Lazy),
2351 environment: Some("test_env".to_string()),
2352 on_terminate: Some(fdecl::OnTerminate::None),
2353 config_overrides: None,
2354 __source_breaking: fidl::marker::SourceBreaking,
2355 },
2356 );
2357 let options: ftest::ChildOptions = ChildOptions::new().reboot_on_terminate().into();
2358 assert_eq!(
2359 options,
2360 ftest::ChildOptions {
2361 startup: Some(fdecl::StartupMode::Lazy),
2362 environment: None,
2363 on_terminate: Some(fdecl::OnTerminate::Reboot),
2364 config_overrides: None,
2365 __source_breaking: fidl::marker::SourceBreaking,
2366 },
2367 );
2368
2369 let mut config_overrides: Vec<fdecl::ConfigOverride> = vec![];
2370 config_overrides.push(fdecl::ConfigOverride {
2371 key: Some("mystring".to_string()),
2372 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::String(
2373 "Fuchsia".to_string(),
2374 ))),
2375 ..Default::default()
2376 });
2377 config_overrides.push(fdecl::ConfigOverride {
2378 key: Some("mynumber".to_string()),
2379 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Uint64(200))),
2380 ..Default::default()
2381 });
2382 let options: ftest::ChildOptions =
2383 ChildOptions::new().config_overrides(config_overrides.clone()).into();
2384 assert_eq!(
2385 options,
2386 ftest::ChildOptions {
2387 startup: Some(fdecl::StartupMode::Lazy),
2388 environment: None,
2389 on_terminate: Some(fdecl::OnTerminate::None),
2390 config_overrides: Some(config_overrides),
2391 __source_breaking: fidl::marker::SourceBreaking,
2392 },
2393 );
2394 }
2395
2396 #[fuchsia::test]
2397 async fn child_scope_prevents_cross_realm_usage() {
2398 let (builder, _server_task, receive_server_requests) = new_realm_builder_and_server_task();
2399 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
2400 let child_realm_b = builder.add_child_realm("b", ChildOptions::new()).await.unwrap();
2401 let child_c = child_realm_b.add_child("c", "test://c", ChildOptions::new()).await.unwrap();
2402
2403 assert_matches!(
2404 builder.add_route(
2405 Route::new()
2406 .capability(Capability::protocol::<fcomponent::RealmMarker>())
2407 .from(&child_a)
2408 .to(&child_c)
2409 ).await,
2410 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_c).into()
2411 );
2412
2413 assert_matches!(
2414 child_realm_b.add_route(
2415 Route::new()
2416 .capability(Capability::protocol::<fcomponent::RealmMarker>())
2417 .from(&child_a)
2418 .to(&child_c)
2419 ).await,
2420 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_a).into()
2421 );
2422
2423 assert_matches!(
2424 builder.get_component_decl(&child_c).await,
2425 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_c).into()
2426 );
2427
2428 assert_matches!(
2429 child_realm_b.get_component_decl(&child_a).await,
2430 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_a).into()
2431 );
2432
2433 assert_matches!(
2434 builder.replace_component_decl(&child_c, cm_rust::ComponentDecl::default()).await,
2435 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_c).into()
2436 );
2437
2438 assert_matches!(
2439 child_realm_b.replace_component_decl(&child_a, cm_rust::ComponentDecl::default()).await,
2440 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_a).into()
2441 );
2442
2443 let sub_realm_receiver = confirm_num_server_requests(receive_server_requests, 2).remove(0);
2446 confirm_num_server_requests(sub_realm_receiver, 1);
2447 }
2448
2449 #[fuchsia::test]
2450 async fn child_ref_construction() {
2451 let (builder, _server_task, receive_server_requests) = new_realm_builder_and_server_task();
2452 let child_realm_a = builder.add_child_realm("a", ChildOptions::new()).await.unwrap();
2453 let child_realm_b = child_realm_a.add_child_realm("b", ChildOptions::new()).await.unwrap();
2454
2455 let child_ref_a: ChildRef = (&child_realm_a).into();
2456 let child_ref_b: ChildRef = (&child_realm_b).into();
2457
2458 assert_eq!(child_ref_a, ChildRef::new("a".to_string(), vec![]),);
2459
2460 assert_eq!(child_ref_b, ChildRef::new("b".to_string(), vec!["a".to_string()]),);
2461
2462 let child_ref_c = builder.add_child("c", "test://c", ChildOptions::new()).await.unwrap();
2463 let child_ref_d =
2464 child_realm_a.add_child("d", "test://d", ChildOptions::new()).await.unwrap();
2465 let child_ref_e =
2466 child_realm_b.add_child("e", "test://e", ChildOptions::new()).await.unwrap();
2467
2468 assert_eq!(child_ref_c, ChildRef::new("c".to_string(), vec![]),);
2469
2470 assert_eq!(child_ref_d, ChildRef::new("d".to_string(), vec!["a".to_string()]),);
2471
2472 assert_eq!(
2473 child_ref_e,
2474 ChildRef::new("e".to_string(), vec!["a".to_string(), "b".to_string()]),
2475 );
2476
2477 confirm_num_server_requests(receive_server_requests, 2);
2480 }
2481
2482 #[fuchsia::test]
2483 async fn protocol_capability_construction() {
2484 assert_eq!(
2485 Capability::protocol_by_name("test"),
2486 ProtocolCapability {
2487 name: "test".to_string(),
2488 as_: None,
2489 type_: fdecl::DependencyType::Strong,
2490 path: None,
2491 availability: None,
2492 },
2493 );
2494 assert_eq!(
2495 Capability::protocol::<ftest::RealmBuilderFactoryMarker>(),
2496 ProtocolCapability {
2497 name: ftest::RealmBuilderFactoryMarker::PROTOCOL_NAME.to_string(),
2498 as_: None,
2499 type_: fdecl::DependencyType::Strong,
2500 path: None,
2501 availability: None,
2502 },
2503 );
2504 assert_eq!(
2505 Capability::protocol_by_name("test").as_("test2"),
2506 ProtocolCapability {
2507 name: "test".to_string(),
2508 as_: Some("test2".to_string()),
2509 type_: fdecl::DependencyType::Strong,
2510 path: None,
2511 availability: None,
2512 },
2513 );
2514 assert_eq!(
2515 Capability::protocol_by_name("test").weak(),
2516 ProtocolCapability {
2517 name: "test".to_string(),
2518 as_: None,
2519 type_: fdecl::DependencyType::Weak,
2520 path: None,
2521 availability: None,
2522 },
2523 );
2524 assert_eq!(
2525 Capability::protocol_by_name("test").path("/svc/test2"),
2526 ProtocolCapability {
2527 name: "test".to_string(),
2528 as_: None,
2529 type_: fdecl::DependencyType::Strong,
2530 path: Some("/svc/test2".to_string()),
2531 availability: None,
2532 },
2533 );
2534 assert_eq!(
2535 Capability::protocol_by_name("test").optional(),
2536 ProtocolCapability {
2537 name: "test".to_string(),
2538 as_: None,
2539 type_: fdecl::DependencyType::Strong,
2540 path: None,
2541 availability: Some(fdecl::Availability::Optional),
2542 },
2543 );
2544 assert_eq!(
2545 Capability::protocol_by_name("test").availability_same_as_target(),
2546 ProtocolCapability {
2547 name: "test".to_string(),
2548 as_: None,
2549 type_: fdecl::DependencyType::Strong,
2550 path: None,
2551 availability: Some(fdecl::Availability::SameAsTarget),
2552 },
2553 );
2554 }
2555
2556 #[fuchsia::test]
2557 async fn directory_capability_construction() {
2558 assert_eq!(
2559 Capability::directory("test"),
2560 DirectoryCapability {
2561 name: "test".to_string(),
2562 as_: None,
2563 type_: fdecl::DependencyType::Strong,
2564 rights: None,
2565 subdir: None,
2566 path: None,
2567 availability: None,
2568 },
2569 );
2570 assert_eq!(
2571 Capability::directory("test").as_("test2"),
2572 DirectoryCapability {
2573 name: "test".to_string(),
2574 as_: Some("test2".to_string()),
2575 type_: fdecl::DependencyType::Strong,
2576 rights: None,
2577 subdir: None,
2578 path: None,
2579 availability: None,
2580 },
2581 );
2582 assert_eq!(
2583 Capability::directory("test").weak(),
2584 DirectoryCapability {
2585 name: "test".to_string(),
2586 as_: None,
2587 type_: fdecl::DependencyType::Weak,
2588 rights: None,
2589 subdir: None,
2590 path: None,
2591 availability: None,
2592 },
2593 );
2594 assert_eq!(
2595 Capability::directory("test").rights(fio::RX_STAR_DIR),
2596 DirectoryCapability {
2597 name: "test".to_string(),
2598 as_: None,
2599 type_: fdecl::DependencyType::Strong,
2600 rights: Some(fio::RX_STAR_DIR),
2601 subdir: None,
2602 path: None,
2603 availability: None,
2604 },
2605 );
2606 assert_eq!(
2607 Capability::directory("test").subdir("test2"),
2608 DirectoryCapability {
2609 name: "test".to_string(),
2610 as_: None,
2611 type_: fdecl::DependencyType::Strong,
2612 rights: None,
2613 subdir: Some("test2".to_string()),
2614 path: None,
2615 availability: None,
2616 },
2617 );
2618 assert_eq!(
2619 Capability::directory("test").path("/test2"),
2620 DirectoryCapability {
2621 name: "test".to_string(),
2622 as_: None,
2623 type_: fdecl::DependencyType::Strong,
2624 rights: None,
2625 subdir: None,
2626 path: Some("/test2".to_string()),
2627 availability: None,
2628 },
2629 );
2630 assert_eq!(
2631 Capability::directory("test").optional(),
2632 DirectoryCapability {
2633 name: "test".to_string(),
2634 as_: None,
2635 type_: fdecl::DependencyType::Strong,
2636 rights: None,
2637 subdir: None,
2638 path: None,
2639 availability: Some(fdecl::Availability::Optional),
2640 },
2641 );
2642 assert_eq!(
2643 Capability::directory("test").availability_same_as_target(),
2644 DirectoryCapability {
2645 name: "test".to_string(),
2646 as_: None,
2647 type_: fdecl::DependencyType::Strong,
2648 rights: None,
2649 subdir: None,
2650 path: None,
2651 availability: Some(fdecl::Availability::SameAsTarget),
2652 },
2653 );
2654 }
2655
2656 #[fuchsia::test]
2657 async fn storage_capability_construction() {
2658 assert_eq!(
2659 Capability::storage("test"),
2660 StorageCapability {
2661 name: "test".to_string(),
2662 as_: None,
2663 path: None,
2664 availability: None
2665 },
2666 );
2667 assert_eq!(
2668 Capability::storage("test").as_("test2"),
2669 StorageCapability {
2670 name: "test".to_string(),
2671 as_: Some("test2".to_string()),
2672 path: None,
2673 availability: None,
2674 },
2675 );
2676 assert_eq!(
2677 Capability::storage("test").path("/test2"),
2678 StorageCapability {
2679 name: "test".to_string(),
2680 as_: None,
2681 path: Some("/test2".to_string()),
2682 availability: None,
2683 },
2684 );
2685 assert_eq!(
2686 Capability::storage("test").optional(),
2687 StorageCapability {
2688 name: "test".to_string(),
2689 as_: None,
2690 path: None,
2691 availability: Some(fdecl::Availability::Optional),
2692 },
2693 );
2694 assert_eq!(
2695 Capability::storage("test").availability_same_as_target(),
2696 StorageCapability {
2697 name: "test".to_string(),
2698 as_: None,
2699 path: None,
2700 availability: Some(fdecl::Availability::SameAsTarget),
2701 },
2702 );
2703 }
2704
2705 #[fuchsia::test]
2706 async fn service_capability_construction() {
2707 assert_eq!(
2708 Capability::service_by_name("test"),
2709 ServiceCapability {
2710 name: "test".to_string(),
2711 as_: None,
2712 path: None,
2713 availability: None
2714 },
2715 );
2716 assert_eq!(
2717 Capability::service_by_name("test").as_("test2"),
2718 ServiceCapability {
2719 name: "test".to_string(),
2720 as_: Some("test2".to_string()),
2721 path: None,
2722 availability: None,
2723 },
2724 );
2725 assert_eq!(
2726 Capability::service_by_name("test").path("/svc/test2"),
2727 ServiceCapability {
2728 name: "test".to_string(),
2729 as_: None,
2730 path: Some("/svc/test2".to_string()),
2731 availability: None,
2732 },
2733 );
2734 assert_eq!(
2735 Capability::service_by_name("test").optional(),
2736 ServiceCapability {
2737 name: "test".to_string(),
2738 as_: None,
2739 path: None,
2740 availability: Some(fdecl::Availability::Optional),
2741 },
2742 );
2743 assert_eq!(
2744 Capability::service_by_name("test").availability_same_as_target(),
2745 ServiceCapability {
2746 name: "test".to_string(),
2747 as_: None,
2748 path: None,
2749 availability: Some(fdecl::Availability::SameAsTarget),
2750 },
2751 );
2752 }
2753
2754 #[fuchsia::test]
2755 async fn dictionary_capability_construction() {
2756 assert_eq!(
2757 Capability::dictionary("test"),
2758 DictionaryCapability {
2759 name: "test".to_string(),
2760 as_: None,
2761 availability: None,
2762 path: None
2763 },
2764 );
2765 assert_eq!(
2766 Capability::dictionary("test").as_("test2"),
2767 DictionaryCapability {
2768 name: "test".to_string(),
2769 as_: Some("test2".to_string()),
2770 availability: None,
2771 path: None,
2772 },
2773 );
2774 assert_eq!(
2775 Capability::dictionary("test").optional(),
2776 DictionaryCapability {
2777 name: "test".to_string(),
2778 as_: None,
2779 availability: Some(fdecl::Availability::Optional),
2780 path: None,
2781 },
2782 );
2783 assert_eq!(
2784 Capability::dictionary("test").availability_same_as_target(),
2785 DictionaryCapability {
2786 name: "test".to_string(),
2787 as_: None,
2788 availability: Some(fdecl::Availability::SameAsTarget),
2789 path: None,
2790 },
2791 );
2792 assert_eq!(
2793 Capability::dictionary("test").path("/data"),
2794 DictionaryCapability {
2795 name: "test".to_string(),
2796 as_: None,
2797 availability: None,
2798 path: Some("/data".to_string()),
2799 },
2800 );
2801 }
2802
2803 #[fuchsia::test]
2804 async fn route_construction() {
2805 assert_eq!(
2806 Route::new()
2807 .capability(Capability::protocol_by_name("test"))
2808 .capability(Capability::protocol_by_name("test2"))
2809 .from(Ref::child("a"))
2810 .to(Ref::collection("b"))
2811 .to(Ref::parent()),
2812 Route {
2813 capabilities: vec![
2814 Capability::protocol_by_name("test").into(),
2815 Capability::protocol_by_name("test2").into(),
2816 ],
2817 from: Some(Ref::child("a").into()),
2818 to: vec![Ref::collection("b").into(), Ref::parent().into(),],
2819 },
2820 );
2821 }
2822
2823 #[derive(Debug)]
2824 enum ServerRequest {
2825 AddChild {
2826 name: String,
2827 url: String,
2828 options: ftest::ChildOptions,
2829 },
2830 AddChildFromDecl {
2831 name: String,
2832 decl: fdecl::Component,
2833 options: ftest::ChildOptions,
2834 },
2835 AddLocalChild {
2836 name: String,
2837 options: ftest::ChildOptions,
2838 },
2839 AddChildRealm {
2840 name: String,
2841 options: ftest::ChildOptions,
2842 receive_requests: mpsc::UnboundedReceiver<ServerRequest>,
2843 },
2844 AddChildRealmFromRelativeUrl {
2845 name: String,
2846 relative_url: String,
2847 options: ftest::ChildOptions,
2848 receive_requests: mpsc::UnboundedReceiver<ServerRequest>,
2849 },
2850 AddChildRealmFromDecl {
2851 name: String,
2852 decl: fdecl::Component,
2853 options: ftest::ChildOptions,
2854 receive_requests: mpsc::UnboundedReceiver<ServerRequest>,
2855 },
2856 GetComponentDecl {
2857 name: String,
2858 },
2859 ReplaceComponentDecl {
2860 name: String,
2861 component_decl: fdecl::Component,
2862 },
2863 UseNestedComponentManager {
2864 #[allow(unused)]
2865 component_manager_relative_url: String,
2866 },
2867 GetRealmDecl,
2868 ReplaceRealmDecl {
2869 component_decl: fdecl::Component,
2870 },
2871 AddRouteFromDictionary {
2872 capabilities: Vec<ftest::Capability>,
2873 from: fdecl::Ref,
2874 from_dictionary: String,
2875 to: Vec<fdecl::Ref>,
2876 },
2877 ReadOnlyDirectory {
2878 name: String,
2879 to: Vec<fdecl::Ref>,
2880 },
2881 AddStorage {
2882 name: String,
2883 to: Vec<fdecl::Ref>,
2884 },
2885 InitMutableConfigFromPackage {
2886 name: String,
2887 },
2888 InitMutableConfigToEmpty {
2889 name: String,
2890 },
2891 AddCapability {
2892 capability: fdecl::Capability,
2893 },
2894 AddCollection {
2895 collection: fdecl::Collection,
2896 },
2897 AddEnvironment {
2898 environment: fdecl::Environment,
2899 },
2900 SetConfigValue {
2901 name: String,
2902 key: String,
2903 value: fdecl::ConfigValueSpec,
2904 },
2905 }
2906
2907 fn handle_realm_stream(
2908 mut stream: ftest::RealmRequestStream,
2909 mut report_requests: mpsc::UnboundedSender<ServerRequest>,
2910 ) -> BoxFuture<'static, ()> {
2911 async move {
2912 let mut child_realm_streams = vec![];
2913 while let Some(req) = stream.try_next().await.unwrap() {
2914 match req {
2915 ftest::RealmRequest::AddChild { responder, name, url, options } => {
2916 report_requests
2917 .send(ServerRequest::AddChild { name, url, options })
2918 .await
2919 .unwrap();
2920 responder.send(Ok(())).unwrap();
2921 }
2922 ftest::RealmRequest::AddChildFromDecl { responder, name, decl, options } => {
2923 report_requests
2924 .send(ServerRequest::AddChildFromDecl { name, decl, options })
2925 .await
2926 .unwrap();
2927 responder.send(Ok(())).unwrap();
2928 }
2929 ftest::RealmRequest::AddLocalChild { responder, name, options } => {
2930 report_requests
2931 .send(ServerRequest::AddLocalChild { name, options })
2932 .await
2933 .unwrap();
2934 responder.send(Ok(())).unwrap();
2935 }
2936 ftest::RealmRequest::AddChildRealm {
2937 responder,
2938 child_realm,
2939 name,
2940 options,
2941 } => {
2942 let (child_realm_report_requests, receive_requests) = mpsc::unbounded();
2943
2944 report_requests
2945 .send(ServerRequest::AddChildRealm { name, options, receive_requests })
2946 .await
2947 .unwrap();
2948
2949 let child_realm_stream = child_realm.into_stream();
2950 child_realm_streams.push(fasync::Task::spawn(async move {
2951 handle_realm_stream(child_realm_stream, child_realm_report_requests)
2952 .await
2953 }));
2954 responder.send(Ok(())).unwrap();
2955 }
2956 ftest::RealmRequest::AddChildRealmFromRelativeUrl {
2957 responder,
2958 child_realm,
2959 name,
2960 relative_url,
2961 options,
2962 } => {
2963 let (child_realm_report_requests, receive_requests) = mpsc::unbounded();
2964
2965 report_requests
2966 .send(ServerRequest::AddChildRealmFromRelativeUrl {
2967 name,
2968 relative_url,
2969 options,
2970 receive_requests,
2971 })
2972 .await
2973 .unwrap();
2974
2975 let child_realm_stream = child_realm.into_stream();
2976 child_realm_streams.push(fasync::Task::spawn(async move {
2977 handle_realm_stream(child_realm_stream, child_realm_report_requests)
2978 .await
2979 }));
2980 responder.send(Ok(())).unwrap();
2981 }
2982 ftest::RealmRequest::AddChildRealmFromDecl {
2983 name,
2984 decl,
2985 options,
2986 child_realm,
2987 responder,
2988 } => {
2989 let (child_realm_report_requests, receive_requests) = mpsc::unbounded();
2990
2991 report_requests
2992 .send(ServerRequest::AddChildRealmFromDecl {
2993 name,
2994 decl,
2995 options,
2996 receive_requests,
2997 })
2998 .await
2999 .unwrap();
3000
3001 let child_realm_stream = child_realm.into_stream();
3002 child_realm_streams.push(fasync::Task::spawn(async move {
3003 handle_realm_stream(child_realm_stream, child_realm_report_requests)
3004 .await
3005 }));
3006 responder.send(Ok(())).unwrap();
3007 }
3008 ftest::RealmRequest::GetComponentDecl { responder, name } => {
3009 report_requests
3010 .send(ServerRequest::GetComponentDecl { name })
3011 .await
3012 .unwrap();
3013 responder.send(Ok(&fdecl::Component::default())).unwrap();
3014 }
3015 ftest::RealmRequest::UseNestedComponentManager {
3016 responder,
3017 component_manager_relative_url,
3018 } => {
3019 report_requests
3020 .send(ServerRequest::UseNestedComponentManager {
3021 component_manager_relative_url,
3022 })
3023 .await
3024 .unwrap();
3025 responder.send(Ok(())).unwrap();
3026 }
3027 ftest::RealmRequest::ReplaceComponentDecl {
3028 responder,
3029 name,
3030 component_decl,
3031 } => {
3032 report_requests
3033 .send(ServerRequest::ReplaceComponentDecl { name, component_decl })
3034 .await
3035 .unwrap();
3036 responder.send(Ok(())).unwrap();
3037 }
3038 ftest::RealmRequest::GetRealmDecl { responder } => {
3039 report_requests.send(ServerRequest::GetRealmDecl).await.unwrap();
3040 responder.send(Ok(&fdecl::Component::default())).unwrap();
3041 }
3042 ftest::RealmRequest::ReplaceRealmDecl { responder, component_decl } => {
3043 report_requests
3044 .send(ServerRequest::ReplaceRealmDecl { component_decl })
3045 .await
3046 .unwrap();
3047 responder.send(Ok(())).unwrap();
3048 }
3049 ftest::RealmRequest::AddRoute { .. } => {
3050 panic!("AddRoute is deprecated");
3051 }
3052 ftest::RealmRequest::AddRouteFromDictionary {
3053 responder,
3054 capabilities,
3055 from,
3056 from_dictionary,
3057 to,
3058 } => {
3059 report_requests
3060 .send(ServerRequest::AddRouteFromDictionary {
3061 capabilities,
3062 from,
3063 from_dictionary,
3064 to,
3065 })
3066 .await
3067 .unwrap();
3068 responder.send(Ok(())).unwrap();
3069 }
3070 ftest::RealmRequest::ReadOnlyDirectory { responder, name, to, .. } => {
3071 report_requests
3072 .send(ServerRequest::ReadOnlyDirectory { name, to })
3073 .await
3074 .unwrap();
3075 responder.send(Ok(())).unwrap();
3076 }
3077 ftest::RealmRequest::AddStorage { responder, name, to, .. } => {
3078 report_requests.send(ServerRequest::AddStorage { name, to }).await.unwrap();
3079 responder.send(Ok(())).unwrap();
3080 }
3081 ftest::RealmRequest::InitMutableConfigFromPackage { name, responder } => {
3082 report_requests
3083 .send(ServerRequest::InitMutableConfigFromPackage { name })
3084 .await
3085 .unwrap();
3086 responder.send(Ok(())).unwrap();
3087 }
3088 ftest::RealmRequest::InitMutableConfigToEmpty { name, responder } => {
3089 report_requests
3090 .send(ServerRequest::InitMutableConfigToEmpty { name })
3091 .await
3092 .unwrap();
3093 responder.send(Ok(())).unwrap();
3094 }
3095 ftest::RealmRequest::AddCapability { capability, responder } => {
3096 report_requests
3097 .send(ServerRequest::AddCapability { capability })
3098 .await
3099 .unwrap();
3100 responder.send(Ok(())).unwrap();
3101 }
3102 ftest::RealmRequest::AddCollection { collection, responder } => {
3103 report_requests
3104 .send(ServerRequest::AddCollection { collection })
3105 .await
3106 .unwrap();
3107 responder.send(Ok(())).unwrap();
3108 }
3109 ftest::RealmRequest::AddEnvironment { environment, responder } => {
3110 report_requests
3111 .send(ServerRequest::AddEnvironment { environment })
3112 .await
3113 .unwrap();
3114 responder.send(Ok(())).unwrap();
3115 }
3116 ftest::RealmRequest::SetConfigValue { responder, name, key, value } => {
3117 report_requests
3118 .send(ServerRequest::SetConfigValue { name, key, value })
3119 .await
3120 .unwrap();
3121 responder.send(Ok(())).unwrap();
3122 }
3123 }
3124 }
3125 }
3126 .boxed()
3127 }
3128
3129 fn new_realm_builder_and_server_task()
3130 -> (RealmBuilder, fasync::Task<()>, mpsc::UnboundedReceiver<ServerRequest>) {
3131 let (realm_proxy, realm_stream) = create_proxy_and_stream::<ftest::RealmMarker>();
3132 let (builder_proxy, mut builder_stream) = create_proxy_and_stream::<ftest::BuilderMarker>();
3133
3134 let builder_task = fasync::Task::spawn(async move {
3135 while let Some(req) = builder_stream.try_next().await.unwrap() {
3136 match req {
3137 ftest::BuilderRequest::Build { runner, responder } => {
3138 drop(runner);
3139 responder.send(Ok("test://hippo")).unwrap();
3140 }
3141 }
3142 }
3143 });
3144
3145 let (realm_report_requests, realm_receive_requests) = mpsc::unbounded();
3146 let server_task = fasync::Task::spawn(async move {
3147 let _builder_task = builder_task;
3148 handle_realm_stream(realm_stream, realm_report_requests).await
3149 });
3150 let id: u64 = rand::random();
3151 let realm_name = format!("auto-{:x}", id);
3152 let component_realm_proxy =
3153 fclient::connect_to_protocol::<fcomponent::RealmMarker>().unwrap();
3154
3155 (
3156 RealmBuilder::build_struct(
3157 component_realm_proxy,
3158 realm_proxy,
3159 builder_proxy,
3160 crate::DEFAULT_COLLECTION_NAME.to_string(),
3161 false,
3162 realm_name,
3163 )
3164 .unwrap(),
3165 server_task,
3166 realm_receive_requests,
3167 )
3168 }
3169
3170 fn confirm_num_server_requests(
3173 mut server_requests: mpsc::UnboundedReceiver<ServerRequest>,
3174 num: usize,
3175 ) -> Vec<mpsc::UnboundedReceiver<ServerRequest>> {
3176 let mut discovered_receivers = vec![];
3177 for i in 0..num {
3178 match server_requests.next().now_or_never() {
3179 Some(Some(ServerRequest::AddChildRealm { receive_requests, .. })) => {
3180 discovered_receivers.push(receive_requests)
3181 }
3182 Some(Some(_)) => (),
3183 Some(None) => panic!("server_requests ended unexpectedly"),
3184 None => panic!("server_requests had less messages in it than we expected: {}", i),
3185 }
3186 }
3187 assert_matches!(server_requests.next().now_or_never(), None);
3188 discovered_receivers
3189 }
3190
3191 fn assert_add_child_realm(
3192 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3193 expected_name: &str,
3194 expected_options: ftest::ChildOptions,
3195 ) -> mpsc::UnboundedReceiver<ServerRequest> {
3196 match receive_server_requests.next().now_or_never() {
3197 Some(Some(ServerRequest::AddChildRealm { name, options, receive_requests }))
3198 if &name == expected_name && options == expected_options =>
3199 {
3200 receive_requests
3201 }
3202 req => panic!("match failed, received unexpected server request: {:?}", req),
3203 }
3204 }
3205
3206 fn assert_add_child_realm_from_relative_url(
3207 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3208 expected_name: &str,
3209 expected_relative_url: &str,
3210 expected_options: ftest::ChildOptions,
3211 ) -> mpsc::UnboundedReceiver<ServerRequest> {
3212 match receive_server_requests.next().now_or_never() {
3213 Some(Some(ServerRequest::AddChildRealmFromRelativeUrl {
3214 name,
3215 relative_url,
3216 options,
3217 receive_requests,
3218 })) if &name == expected_name
3219 && options == expected_options
3220 && relative_url == expected_relative_url =>
3221 {
3222 receive_requests
3223 }
3224 req => panic!("match failed, received unexpected server request: {:?}", req),
3225 }
3226 }
3227
3228 fn assert_add_child_realm_from_decl(
3229 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3230 expected_name: &str,
3231 expected_decl: &fdecl::Component,
3232 expected_options: ftest::ChildOptions,
3233 ) -> mpsc::UnboundedReceiver<ServerRequest> {
3234 match receive_server_requests.next().now_or_never() {
3235 Some(Some(ServerRequest::AddChildRealmFromDecl {
3236 name,
3237 decl,
3238 options,
3239 receive_requests,
3240 })) if &name == expected_name
3241 && options == expected_options
3242 && decl == *expected_decl =>
3243 {
3244 receive_requests
3245 }
3246 req => panic!("match failed, received unexpected server request: {:?}", req),
3247 }
3248 }
3249
3250 fn assert_read_only_directory(
3251 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3252 expected_directory_name: &str,
3253 expected_targets: Vec<impl Into<Ref>>,
3254 ) {
3255 let expected_targets = expected_targets
3256 .into_iter()
3257 .map(|t| {
3258 let t: Ref = t.into();
3259 t.into_fidl(RefContext::Target).0
3260 })
3261 .collect::<Vec<_>>();
3262
3263 match receive_server_requests.next().now_or_never() {
3264 Some(Some(ServerRequest::ReadOnlyDirectory { name, to, .. }))
3265 if &name == expected_directory_name && to == expected_targets =>
3266 {
3267 return;
3268 }
3269 req => panic!("match failed, received unexpected server request: {:?}", req),
3270 }
3271 }
3272
3273 #[fuchsia::test]
3274 async fn add_child() {
3275 let (builder, _server_task, mut receive_server_requests) =
3276 new_realm_builder_and_server_task();
3277 let _child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3278 assert_matches!(
3279 receive_server_requests.next().await,
3280 Some(ServerRequest::AddChild { name, url, options })
3281 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3282 );
3283 assert_matches!(receive_server_requests.next().now_or_never(), None);
3284 }
3285
3286 #[fuchsia::test]
3287 async fn add_child_from_decl() {
3288 let (builder, _server_task, mut receive_server_requests) =
3289 new_realm_builder_and_server_task();
3290 let _child_a = builder
3291 .add_child_from_decl("a", cm_rust::ComponentDecl::default(), ChildOptions::new())
3292 .await
3293 .unwrap();
3294 assert_matches!(
3295 receive_server_requests.next().await,
3296 Some(ServerRequest::AddChildFromDecl { name, decl, options })
3297 if &name == "a"
3298 && decl == fdecl::Component::default()
3299 && options == ChildOptions::new().into()
3300 );
3301 assert_matches!(receive_server_requests.next().now_or_never(), None);
3302 }
3303
3304 #[fuchsia::test]
3305 async fn add_local_child() {
3306 let (builder, _server_task, mut receive_server_requests) =
3307 new_realm_builder_and_server_task();
3308 let _child_a = builder
3309 .add_local_child("a", |_| async move { Ok(()) }.boxed(), ChildOptions::new())
3310 .await
3311 .unwrap();
3312 assert_matches!(
3313 receive_server_requests.next().await,
3314 Some(ServerRequest::AddLocalChild { name, options })
3315 if &name == "a" && options == ChildOptions::new().into()
3316 );
3317 assert_matches!(receive_server_requests.next().now_or_never(), None);
3318 }
3319
3320 #[fuchsia::test]
3321 async fn add_child_realm() {
3322 let (builder, _server_task, mut receive_server_requests) =
3323 new_realm_builder_and_server_task();
3324 let child_realm_a = builder.add_child_realm("a", ChildOptions::new()).await.unwrap();
3325 let _child_b = child_realm_a.add_child("b", "test://b", ChildOptions::new()).await.unwrap();
3326 let child_realm_c = builder
3327 .add_child_realm_from_relative_url("c", "#c", ChildOptions::new())
3328 .await
3329 .unwrap();
3330 let _child_d = child_realm_c.add_child("d", "test://d", ChildOptions::new()).await.unwrap();
3331 let child_realm_e = builder
3332 .add_child_realm_from_decl("e", cm_rust::ComponentDecl::default(), ChildOptions::new())
3333 .await
3334 .unwrap();
3335 let _child_f = child_realm_e.add_child("f", "test://f", ChildOptions::new()).await.unwrap();
3336
3337 let mut receive_sub_realm_requests =
3338 assert_add_child_realm(&mut receive_server_requests, "a", ChildOptions::new().into());
3339 assert_matches!(
3340 receive_sub_realm_requests.next().await,
3341 Some(ServerRequest::AddChild { name, url, options })
3342 if &name == "b" && &url == "test://b" && options == ChildOptions::new().into()
3343 );
3344 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3345
3346 let mut receive_sub_realm_requests = assert_add_child_realm_from_relative_url(
3347 &mut receive_server_requests,
3348 "c",
3349 "#c",
3350 ChildOptions::new().into(),
3351 );
3352 assert_matches!(
3353 receive_sub_realm_requests.next().await,
3354 Some(ServerRequest::AddChild { name, url, options })
3355 if &name == "d" && &url == "test://d" && options == ChildOptions::new().into()
3356 );
3357 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3358
3359 let mut receive_sub_realm_requests = assert_add_child_realm_from_decl(
3360 &mut receive_server_requests,
3361 "e",
3362 &fdecl::Component::default(),
3363 ChildOptions::new().into(),
3364 );
3365 assert_matches!(
3366 receive_sub_realm_requests.next().await,
3367 Some(ServerRequest::AddChild { name, url, options })
3368 if &name == "f" && &url == "test://f" && options == ChildOptions::new().into()
3369 );
3370 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3371 assert_matches!(receive_server_requests.next().now_or_never(), None);
3372 }
3373
3374 #[fuchsia::test]
3375 async fn get_component_decl() {
3376 let (builder, _server_task, mut receive_server_requests) =
3377 new_realm_builder_and_server_task();
3378 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3379 let _decl = builder.get_component_decl(&child_a).await.unwrap();
3380
3381 assert_matches!(
3382 receive_server_requests.next().await,
3383 Some(ServerRequest::AddChild { name, url, options })
3384 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3385 );
3386 assert_matches!(
3387 receive_server_requests.next().await,
3388 Some(ServerRequest::GetComponentDecl { name }) if &name == "a"
3389 );
3390 assert_matches!(receive_server_requests.next().now_or_never(), None);
3391 }
3392
3393 #[fuchsia::test]
3394 async fn replace_component_decl() {
3395 let (builder, _server_task, mut receive_server_requests) =
3396 new_realm_builder_and_server_task();
3397 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3398 builder.replace_component_decl(&child_a, cm_rust::ComponentDecl::default()).await.unwrap();
3399
3400 assert_matches!(
3401 receive_server_requests.next().await,
3402 Some(ServerRequest::AddChild { name, url, options })
3403 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3404 );
3405 assert_matches!(
3406 receive_server_requests.next().await,
3407 Some(ServerRequest::ReplaceComponentDecl { name, component_decl })
3408 if &name == "a" && component_decl == fdecl::Component::default()
3409 );
3410 assert_matches!(receive_server_requests.next().now_or_never(), None);
3411 }
3412
3413 #[fuchsia::test]
3414 async fn get_realm_decl() {
3415 let (builder, _server_task, mut receive_server_requests) =
3416 new_realm_builder_and_server_task();
3417 let _decl = builder.get_realm_decl().await.unwrap();
3418
3419 assert_matches!(receive_server_requests.next().await, Some(ServerRequest::GetRealmDecl));
3420 assert_matches!(receive_server_requests.next().now_or_never(), None);
3421 }
3422
3423 #[fuchsia::test]
3424 async fn replace_realm_decl() {
3425 let (builder, _server_task, mut receive_server_requests) =
3426 new_realm_builder_and_server_task();
3427 builder.replace_realm_decl(cm_rust::ComponentDecl::default()).await.unwrap();
3428
3429 assert_matches!(
3430 receive_server_requests.next().await,
3431 Some(ServerRequest::ReplaceRealmDecl { component_decl })
3432 if component_decl == fdecl::Component::default()
3433 );
3434 assert_matches!(receive_server_requests.next().now_or_never(), None);
3435 }
3436
3437 #[fuchsia::test]
3438 async fn set_config_value() {
3439 let (builder, _server_task, mut receive_server_requests) =
3440 new_realm_builder_and_server_task();
3441 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3442 builder.init_mutable_config_from_package(&child_a).await.unwrap();
3443 builder.init_mutable_config_to_empty(&child_a).await.unwrap();
3444 builder.set_config_value(&child_a, "test_bool", false.into()).await.unwrap();
3445 builder.set_config_value(&child_a, "test_int16", (-2 as i16).into()).await.unwrap();
3446 builder.set_config_value(&child_a, "test_string", "test".to_string().into()).await.unwrap();
3447 builder
3448 .set_config_value(&child_a, "test_string_vector", vec!["hello", "fuchsia"].into())
3449 .await
3450 .unwrap();
3451
3452 assert_matches!(
3453 receive_server_requests.next().await,
3454 Some(ServerRequest::AddChild { name, url, options })
3455 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3456 );
3457
3458 assert_matches!(
3459 receive_server_requests.next().await,
3460 Some(ServerRequest::InitMutableConfigFromPackage { name }) if &name == "a"
3461 );
3462
3463 assert_matches!(
3464 receive_server_requests.next().await,
3465 Some(ServerRequest::InitMutableConfigToEmpty { name }) if &name == "a"
3466 );
3467
3468 assert_matches!(
3469 receive_server_requests.next().await,
3470 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3471 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(boolean))), ..
3472 }}) if &name == "a" && &key == "test_bool" && boolean == false
3473 );
3474
3475 assert_matches!(
3476 receive_server_requests.next().await,
3477 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3478 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Int16(int16))), ..
3479 }}) if &name == "a" && &key == "test_int16" && int16 == -2
3480 );
3481
3482 assert_matches!(
3483 receive_server_requests.next().await,
3484 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3485 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::String(string))), ..
3486 }}) if &name == "a" && &key == "test_string" && &string == "test"
3487 );
3488
3489 assert_matches!(
3490 receive_server_requests.next().await,
3491 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3492 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::StringVector(string_vector))), ..
3493 }}) if &name == "a" && &key == "test_string_vector" && string_vector == vec!["hello", "fuchsia"]
3494 );
3495
3496 assert_matches!(receive_server_requests.next().now_or_never(), None);
3497 }
3498
3499 #[fuchsia::test]
3500 async fn add_route() {
3501 let (builder, _server_task, mut receive_server_requests) =
3502 new_realm_builder_and_server_task();
3503 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3504 builder
3505 .add_route(
3506 Route::new()
3507 .capability(Capability::protocol_by_name("test"))
3508 .capability(Capability::directory("test2"))
3509 .capability(Capability::service_by_name("test3"))
3510 .capability(Capability::configuration("test4"))
3511 .capability(Capability::dictionary("test5"))
3512 .from(&child_a)
3513 .to(Ref::parent()),
3514 )
3515 .await
3516 .unwrap();
3517
3518 assert_matches!(
3519 receive_server_requests.next().await,
3520 Some(ServerRequest::AddChild { name, url, options })
3521 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3522 );
3523 assert_matches!(
3524 receive_server_requests.next().await,
3525 Some(ServerRequest::AddRouteFromDictionary { capabilities, from, to, from_dictionary })
3526 if capabilities == vec![
3527 Capability::protocol_by_name("test").into(),
3528 Capability::directory("test2").into(),
3529 Capability::service_by_name("test3").into(),
3530 Capability::configuration("test4").into(),
3531 Capability::dictionary("test5").into(),
3532 ]
3533 && from == Ref::child("a").into_fidl(RefContext::Source).0
3534 && to == vec![Ref::parent().into_fidl(RefContext::Target).0]
3535 && from_dictionary == "."
3536 );
3537 assert_matches!(receive_server_requests.next().now_or_never(), None);
3538 }
3539
3540 #[fuchsia::test]
3541 async fn add_route_to_dictionary() {
3542 let (builder, _server_task, mut receive_server_requests) =
3543 new_realm_builder_and_server_task();
3544 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3545 builder
3546 .add_capability(cm_rust::CapabilityDecl::Dictionary(cm_rust::DictionaryDecl {
3547 name: "my_dict".parse().unwrap(),
3548 source_path: None,
3549 }))
3550 .await
3551 .unwrap();
3552 builder
3553 .add_route(
3554 Route::new()
3555 .capability(Capability::protocol_by_name("test"))
3556 .capability(Capability::directory("test2"))
3557 .capability(Capability::service_by_name("test3"))
3558 .capability(Capability::dictionary("test4"))
3559 .from(&child_a)
3560 .to(Ref::dictionary(Ref::self_(), "my_dict")),
3561 )
3562 .await
3563 .unwrap();
3564
3565 assert_matches!(
3566 receive_server_requests.next().await,
3567 Some(ServerRequest::AddChild { name, url, options })
3568 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3569 );
3570 assert_matches!(
3571 receive_server_requests.next().await,
3572 Some(ServerRequest::AddCapability { .. })
3573 );
3574 assert_matches!(
3575 receive_server_requests.next().await,
3576 Some(ServerRequest::AddRouteFromDictionary { capabilities, from, to, from_dictionary })
3577 if capabilities == vec![
3578 Capability::protocol_by_name("test").into(),
3579 Capability::directory("test2").into(),
3580 Capability::service_by_name("test3").into(),
3581 Capability::dictionary("test4").into(),
3582 ]
3583 && from == Ref::child("a").into_fidl(RefContext::Source).0
3584 && to == vec![Ref::dictionary(Ref::self_(), "my_dict").into_fidl(RefContext::Target).0]
3585 && from_dictionary == "."
3586 );
3587 assert_matches!(receive_server_requests.next().now_or_never(), None);
3588 }
3589
3590 #[fuchsia::test]
3591 async fn add_route_from_dictionary() {
3592 let (builder, _server_task, mut receive_server_requests) =
3593 new_realm_builder_and_server_task();
3594 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3595 builder
3596 .add_route(
3597 Route::new()
3598 .capability(Capability::protocol_by_name("test"))
3599 .capability(Capability::directory("test2"))
3600 .capability(Capability::service_by_name("test3"))
3601 .capability(Capability::dictionary("test4"))
3602 .from(Ref::dictionary(&child_a, "source/dict"))
3603 .to(Ref::parent()),
3604 )
3605 .await
3606 .unwrap();
3607
3608 assert_matches!(
3609 receive_server_requests.next().await,
3610 Some(ServerRequest::AddChild { name, url, options })
3611 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3612 );
3613 assert_matches!(
3614 receive_server_requests.next().await,
3615 Some(ServerRequest::AddRouteFromDictionary { capabilities, from, to, from_dictionary })
3616 if capabilities == vec![
3617 Capability::protocol_by_name("test").into(),
3618 Capability::directory("test2").into(),
3619 Capability::service_by_name("test3").into(),
3620 Capability::dictionary("test4").into(),
3621 ]
3622 && from == Ref::child("a").into_fidl(RefContext::Source).0
3623 && to == vec![Ref::parent().into_fidl(RefContext::Target).0]
3624 && from_dictionary == "source/dict"
3625 );
3626 assert_matches!(receive_server_requests.next().now_or_never(), None);
3627 }
3628
3629 #[fuchsia::test]
3630 async fn add_child_to_sub_realm() {
3631 let (builder, _server_task, mut receive_server_requests) =
3632 new_realm_builder_and_server_task();
3633 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3634 let _child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3635 let mut receive_sub_realm_requests =
3636 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3637 assert_matches!(
3638 receive_sub_realm_requests.next().await,
3639 Some(ServerRequest::AddChild { name, url, options })
3640 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3641 );
3642 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3643 assert_matches!(receive_server_requests.next().now_or_never(), None);
3644 }
3645
3646 #[fuchsia::test]
3647 async fn add_child_from_decl_to_sub_realm() {
3648 let (builder, _server_task, mut receive_server_requests) =
3649 new_realm_builder_and_server_task();
3650 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3651 let _child_a = child_realm
3652 .add_child_from_decl("a", cm_rust::ComponentDecl::default(), ChildOptions::new())
3653 .await
3654 .unwrap();
3655 let mut receive_sub_realm_requests =
3656 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3657 assert_matches!(
3658 receive_sub_realm_requests.next().await,
3659 Some(ServerRequest::AddChildFromDecl { name, decl, options })
3660 if &name == "a"
3661 && decl == fdecl::Component::default()
3662 && options == ChildOptions::new().into()
3663 );
3664 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3665 assert_matches!(receive_server_requests.next().now_or_never(), None);
3666 }
3667
3668 #[fuchsia::test]
3669 async fn add_local_child_to_sub_realm() {
3670 let (builder, _server_task, mut receive_server_requests) =
3671 new_realm_builder_and_server_task();
3672 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3673 let _child_a = child_realm
3674 .add_local_child("a", |_| async move { Ok(()) }.boxed(), ChildOptions::new())
3675 .await
3676 .unwrap();
3677 let mut receive_sub_realm_requests =
3678 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3679 assert_matches!(
3680 receive_sub_realm_requests.next().await,
3681 Some(ServerRequest::AddLocalChild { name, options })
3682 if &name == "a" && options == ChildOptions::new().into()
3683 );
3684 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3685 assert_matches!(receive_server_requests.next().now_or_never(), None);
3686 }
3687
3688 #[fuchsia::test]
3689 async fn add_child_realm_to_child_realm() {
3690 let (builder, _server_task, mut receive_server_requests) =
3691 new_realm_builder_and_server_task();
3692 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3693 let child_realm_a = child_realm.add_child_realm("a", ChildOptions::new()).await.unwrap();
3694 let _child_b = child_realm_a.add_child("b", "test://b", ChildOptions::new()).await.unwrap();
3695
3696 let mut receive_sub_realm_requests =
3697 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3698 let mut receive_sub_sub_realm_requests = assert_add_child_realm(
3699 &mut receive_sub_realm_requests,
3700 "a",
3701 ChildOptions::new().into(),
3702 );
3703 assert_matches!(
3704 receive_sub_sub_realm_requests.next().await,
3705 Some(ServerRequest::AddChild { name, url, options })
3706 if &name == "b" && &url == "test://b" && options == ChildOptions::new().into()
3707 );
3708 assert_matches!(receive_sub_sub_realm_requests.next().now_or_never(), None);
3709 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3710 assert_matches!(receive_server_requests.next().now_or_never(), None);
3711 }
3712
3713 #[fuchsia::test]
3714 async fn get_component_decl_in_sub_realm() {
3715 let (builder, _server_task, mut receive_server_requests) =
3716 new_realm_builder_and_server_task();
3717 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3718 let child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3719 let _decl = child_realm.get_component_decl(&child_a).await.unwrap();
3720
3721 let mut receive_sub_realm_requests =
3722 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3723 assert_matches!(
3724 receive_sub_realm_requests.next().await,
3725 Some(ServerRequest::AddChild { name, url, options })
3726 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3727 );
3728 assert_matches!(
3729 receive_sub_realm_requests.next().await,
3730 Some(ServerRequest::GetComponentDecl { name }) if &name == "a"
3731 );
3732 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3733 assert_matches!(receive_server_requests.next().now_or_never(), None);
3734 }
3735
3736 #[fuchsia::test]
3737 async fn replace_component_decl_in_sub_realm() {
3738 let (builder, _server_task, mut receive_server_requests) =
3739 new_realm_builder_and_server_task();
3740 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3741 let child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3742 child_realm
3743 .replace_component_decl(&child_a, cm_rust::ComponentDecl::default())
3744 .await
3745 .unwrap();
3746
3747 let mut receive_sub_realm_requests =
3748 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3749 assert_matches!(
3750 receive_sub_realm_requests.next().await,
3751 Some(ServerRequest::AddChild { name, url, options })
3752 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3753 );
3754 assert_matches!(
3755 receive_sub_realm_requests.next().await,
3756 Some(ServerRequest::ReplaceComponentDecl { name, component_decl })
3757 if &name == "a" && component_decl == fdecl::Component::default()
3758 );
3759 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3760 assert_matches!(receive_server_requests.next().now_or_never(), None);
3761 }
3762
3763 #[fuchsia::test]
3764 async fn get_realm_decl_in_sub_realm() {
3765 let (builder, _server_task, mut receive_server_requests) =
3766 new_realm_builder_and_server_task();
3767 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3768 let _decl = child_realm.get_realm_decl().await.unwrap();
3769
3770 let mut receive_sub_realm_requests =
3771 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3772 assert_matches!(receive_sub_realm_requests.next().await, Some(ServerRequest::GetRealmDecl));
3773 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3774 assert_matches!(receive_server_requests.next().now_or_never(), None);
3775 }
3776
3777 #[fuchsia::test]
3778 async fn replace_realm_decl_in_sub_realm() {
3779 let (builder, _server_task, mut receive_server_requests) =
3780 new_realm_builder_and_server_task();
3781 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3782 child_realm.replace_realm_decl(cm_rust::ComponentDecl::default()).await.unwrap();
3783
3784 let mut receive_sub_realm_requests =
3785 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3786 assert_matches!(
3787 receive_sub_realm_requests.next().await,
3788 Some(ServerRequest::ReplaceRealmDecl { component_decl })
3789 if component_decl == fdecl::Component::default()
3790 );
3791 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3792 assert_matches!(receive_server_requests.next().now_or_never(), None);
3793 }
3794
3795 #[fuchsia::test]
3796 async fn add_route_in_sub_realm() {
3797 let (builder, _server_task, mut receive_server_requests) =
3798 new_realm_builder_and_server_task();
3799 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3800 let child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3801 child_realm
3802 .add_route(
3803 Route::new()
3804 .capability(Capability::protocol_by_name("test"))
3805 .capability(Capability::directory("test2"))
3806 .from(&child_a)
3807 .to(Ref::parent()),
3808 )
3809 .await
3810 .unwrap();
3811
3812 let mut receive_sub_realm_requests =
3813 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3814 assert_matches!(
3815 receive_sub_realm_requests.next().await,
3816 Some(ServerRequest::AddChild { name, url, options })
3817 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3818 );
3819 assert_matches!(
3820 receive_sub_realm_requests.next().await,
3821 Some(ServerRequest::AddRouteFromDictionary { capabilities, from, to, from_dictionary })
3822 if capabilities == vec![
3823 Capability::protocol_by_name("test").into(),
3824 Capability::directory("test2").into(),
3825 ]
3826 && from == Ref::child("a").into_fidl(RefContext::Source).0
3827 && to == vec![Ref::parent().into_fidl(RefContext::Target).0]
3828 && from_dictionary == "."
3829 );
3830 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3831 assert_matches!(receive_server_requests.next().now_or_never(), None);
3832 }
3833
3834 #[fuchsia::test]
3835 async fn read_only_directory() {
3836 let (builder, _server_task, mut receive_server_requests) =
3837 new_realm_builder_and_server_task();
3838 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3839 builder
3840 .read_only_directory(
3841 "config",
3842 vec![&child_a],
3843 DirectoryContents::new().add_file("config.json", "{ \"hippos\": \"rule!\" }"),
3844 )
3845 .await
3846 .unwrap();
3847
3848 assert_matches!(
3849 receive_server_requests.next().await,
3850 Some(ServerRequest::AddChild { name, url, options })
3851 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3852 );
3853 assert_read_only_directory(&mut receive_server_requests, "config", vec![&child_a]);
3854 }
3855
3856 #[fuchsia::test]
3857 async fn storage() {
3858 let (builder, _server_task, mut receive_server_requests) =
3859 new_realm_builder_and_server_task();
3860 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3861 builder.add_storage("data", vec![&child_a], None).await.unwrap();
3862
3863 assert_matches!(
3864 receive_server_requests.next().await,
3865 Some(ServerRequest::AddChild { name, url, options })
3866 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3867 );
3868 assert_matches!(
3869 receive_server_requests.next().await,
3870 Some(ServerRequest::AddStorage { name, to })
3871 if &name == "data" && &to == &vec![fdecl::Ref::Child(fdecl::ChildRef { name: "a".to_string(), collection: None })]
3872 );
3873 }
3874
3875 #[test]
3876 fn realm_builder_works_with_send() {
3877 let mut executor = fasync::SendExecutorBuilder::new().num_threads(2).build();
3880 executor.run(async {
3881 let (builder, _server_task, _receive_server_requests) =
3882 new_realm_builder_and_server_task();
3883 let child_realm_a = builder.add_child_realm("a", ChildOptions::new()).await.unwrap();
3884 let child_b = builder
3885 .add_local_child("b", |_handles| pending().boxed(), ChildOptions::new())
3886 .await
3887 .unwrap();
3888 let child_c = builder.add_child("c", "test://c", ChildOptions::new()).await.unwrap();
3889 let child_e = builder
3890 .add_child_from_decl("e", cm_rust::ComponentDecl::default(), ChildOptions::new())
3891 .await
3892 .unwrap();
3893
3894 let decl_for_e = builder.get_component_decl(&child_e).await.unwrap();
3895 builder.replace_component_decl(&child_e, decl_for_e).await.unwrap();
3896 let realm_decl = builder.get_realm_decl().await.unwrap();
3897 builder.replace_realm_decl(realm_decl).await.unwrap();
3898 builder
3899 .add_route(
3900 Route::new()
3901 .capability(Capability::protocol::<fcomponent::RealmMarker>())
3902 .from(&child_e)
3903 .to(&child_c)
3904 .to(&child_b)
3905 .to(&child_realm_a)
3906 .to(Ref::parent()),
3907 )
3908 .await
3909 .unwrap();
3910 builder
3911 .read_only_directory(
3912 "config",
3913 vec![&child_e],
3914 DirectoryContents::new().add_file("config.json", "{ \"hippos\": \"rule!\" }"),
3915 )
3916 .await
3917 .unwrap();
3918 });
3919 }
3920
3921 #[fuchsia::test]
3922 async fn add_configurations() {
3923 let (builder, _server_task, mut receive_server_requests) =
3924 new_realm_builder_and_server_task();
3925 _ = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3926 _ = receive_server_requests.next().now_or_never();
3927
3928 builder
3929 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
3930 name: "my-config".to_string().fidl_into_native(),
3931 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(true)),
3932 }))
3933 .await
3934 .unwrap();
3935 match receive_server_requests.next().now_or_never() {
3936 Some(Some(ServerRequest::AddCapability { capability, .. })) => {
3937 let configuration = assert_matches!(capability, fdecl::Capability::Config(c) => c);
3938 assert_eq!(configuration.name, Some("my-config".to_string()));
3939 assert_eq!(
3940 configuration.value,
3941 Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true)))
3942 );
3943 }
3944 req => panic!("match failed, received unexpected server request: {:?}", req),
3945 };
3946 }
3947
3948 #[fuchsia::test]
3949 async fn add_environment_and_collection() {
3950 let (builder, _server_task, mut receive_server_requests) =
3951 new_realm_builder_and_server_task();
3952 _ = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3953 _ = receive_server_requests.next().now_or_never();
3954
3955 builder
3956 .add_environment(cm_rust::EnvironmentDecl {
3957 name: "driver-host-env".parse().unwrap(),
3958 extends: fdecl::EnvironmentExtends::Realm,
3959 runners: Box::from([]),
3960 resolvers: Box::from([cm_rust::ResolverRegistration {
3961 resolver: "boot-resolver".parse().unwrap(),
3962 source: cm_rust::RegistrationSource::Child("fake-resolver".to_string()),
3963 scheme: "fuchsia-boot".to_string(),
3964 }]),
3965 debug_capabilities: Box::from([]),
3966 stop_timeout_ms: Some(20000),
3967 })
3968 .await
3969 .unwrap();
3970 match receive_server_requests.next().now_or_never() {
3971 Some(Some(ServerRequest::AddEnvironment { environment, .. })) => {
3972 assert_eq!(environment.name, Some("driver-host-env".to_string()));
3973 }
3974 req => panic!("match failed, received unexpected server request: {:?}", req),
3975 };
3976 builder
3977 .add_collection(cm_rust::CollectionDecl {
3978 name: "driver-hosts".parse().unwrap(),
3979 durability: fdecl::Durability::SingleRun,
3980 environment: Some("driver-host-env".parse().unwrap()),
3981 allowed_offers: Default::default(),
3982 allow_long_names: Default::default(),
3983 persistent_storage: None,
3984 })
3985 .await
3986 .unwrap();
3987 match receive_server_requests.next().now_or_never() {
3988 Some(Some(ServerRequest::AddCollection { collection, .. })) => {
3989 assert_eq!(collection.name, Some("driver-hosts".to_string()));
3990 }
3991 req => panic!("match failed, received unexpected server request: {:?}", req),
3992 };
3993 }
3994}