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 }
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}
759
760impl DictionaryCapability {
761 pub fn as_(mut self, as_: impl Into<String>) -> Self {
763 self.as_ = Some(as_.into());
764 self
765 }
766
767 pub fn optional(mut self) -> Self {
770 self.availability = Some(fdecl::Availability::Optional);
771 self
772 }
773
774 pub fn availability_same_as_target(mut self) -> Self {
777 self.availability = Some(fdecl::Availability::SameAsTarget);
778 self
779 }
780}
781
782impl Into<ftest::Capability> for DictionaryCapability {
783 fn into(self) -> ftest::Capability {
784 ftest::Capability::Dictionary(ftest::Dictionary {
785 name: Some(self.name),
786 as_: self.as_,
787 availability: self.availability,
788 ..Default::default()
789 })
790 }
791}
792
793#[derive(Debug, Clone, PartialEq)]
796pub struct ResolverCapability {
797 name: String,
798 as_: Option<String>,
799 path: Option<String>,
800}
801
802impl ResolverCapability {
803 pub fn as_(mut self, as_: impl Into<String>) -> Self {
805 self.as_ = Some(as_.into());
806 self
807 }
808
809 pub fn path(mut self, path: impl Into<String>) -> Self {
813 self.path = Some(path.into());
814 self
815 }
816}
817
818impl Into<ftest::Capability> for ResolverCapability {
819 fn into(self) -> ftest::Capability {
820 ftest::Capability::Resolver(ftest::Resolver {
821 name: Some(self.name),
822 as_: self.as_,
823 path: self.path,
824 ..Default::default()
825 })
826 }
827}
828
829#[derive(Debug, Clone, PartialEq)]
832pub struct RunnerCapability {
833 name: String,
834 as_: Option<String>,
835 path: Option<String>,
836}
837
838impl RunnerCapability {
839 pub fn as_(mut self, as_: impl Into<String>) -> Self {
841 self.as_ = Some(as_.into());
842 self
843 }
844
845 pub fn path(mut self, path: impl Into<String>) -> Self {
849 self.path = Some(path.into());
850 self
851 }
852}
853
854impl Into<ftest::Capability> for RunnerCapability {
855 fn into(self) -> ftest::Capability {
856 ftest::Capability::Runner(ftest::Runner {
857 name: Some(self.name),
858 as_: self.as_,
859 path: self.path,
860 ..Default::default()
861 })
862 }
863}
864
865#[derive(Debug, Clone, PartialEq)]
867pub struct Route {
868 capabilities: Vec<ftest::Capability>,
869 from: Option<Ref>,
870 to: Vec<Ref>,
871}
872
873impl Route {
874 pub fn new() -> Self {
875 Self { capabilities: vec![], from: None, to: vec![] }
876 }
877
878 pub fn capability(mut self, capability: impl Into<ftest::Capability>) -> Self {
880 self.capabilities.push(capability.into());
881 self
882 }
883
884 pub fn from(mut self, from: impl Into<Ref>) -> Self {
897 let from = from.into();
898 if self.from.is_some() {
899 panic!("from is already set for this route");
900 }
901 self.from = Some(from);
902 self
903 }
904
905 pub fn to(mut self, to: impl Into<Ref>) -> Self {
907 self.to.push(to.into());
908 self
909 }
910}
911
912pub struct RealmInstance {
915 pub root: ScopedInstance,
918
919 local_component_runner_task: Option<fasync::Task<()>>,
923}
924
925impl Drop for RealmInstance {
926 fn drop(&mut self) {
933 if !self.root.destroy_waiter_taken() {
934 let destroy_waiter = self.root.take_destroy_waiter();
935 let local_component_runner_task = self.local_component_runner_task.take();
936 fasync::Task::spawn(async move {
937 let _local_component_runner_task = local_component_runner_task;
939 let _ = destroy_waiter.await;
942 })
943 .detach();
944 }
945 debug!("RealmInstance is now shut down - the realm will be destroyed.");
948 }
949}
950
951impl RealmInstance {
952 pub async fn destroy(mut self) -> Result<(), Error> {
958 if self.root.destroy_waiter_taken() {
959 return Err(Error::DestroyWaiterTaken);
960 }
961 let _local_component_runner_task = self.local_component_runner_task.take();
962 let destroy_waiter = self.root.take_destroy_waiter();
963 drop(self);
964 destroy_waiter.await.map_err(Error::FailedToDestroyChild)?;
965 Ok(())
966 }
967
968 pub async fn start_component_tree(&self) -> Result<(), Error> {
972 let lifecycle_controller: fsys::LifecycleControllerProxy = self
973 .root
974 .connect_to_protocol_at_exposed_dir()
975 .map_err(|e| Error::CannotStartRootComponent(e))?;
976 let (_, binder_server) = fidl::endpoints::create_endpoints::<fcomponent::BinderMarker>();
977 lifecycle_controller.start_instance("./", binder_server).await?.map_err(|e| {
978 Error::CannotStartRootComponent(format_err!("received error status: {:?}", e))
979 })?;
980 Ok(())
981 }
982}
983
984#[derive(Debug, Clone)]
985pub struct RealmBuilderParams {
986 component_realm_proxy: Option<fcomponent::RealmProxy>,
987 collection_name: Option<String>,
988 fragment_only_url: Option<String>,
989 pkg_dir_proxy: Option<fio::DirectoryProxy>,
990 start: bool,
991 realm_name: Option<String>,
992}
993
994impl RealmBuilderParams {
995 pub fn new() -> Self {
996 Self {
997 component_realm_proxy: None,
998 collection_name: None,
999 fragment_only_url: None,
1000 pkg_dir_proxy: None,
1001 start: true,
1002 realm_name: None,
1003 }
1004 }
1005
1006 pub fn with_realm_proxy(mut self, realm_proxy: fcomponent::RealmProxy) -> Self {
1007 self.component_realm_proxy = Some(realm_proxy);
1008 self
1009 }
1010
1011 pub fn in_collection(mut self, collection_name: impl Into<String>) -> Self {
1012 self.collection_name = Some(collection_name.into());
1013 self
1014 }
1015
1016 pub fn from_relative_url(mut self, fragment_only_url: impl Into<String>) -> Self {
1017 self.fragment_only_url = Some(fragment_only_url.into());
1018 self
1019 }
1020
1021 pub fn with_pkg_dir_proxy(mut self, pkg_dir_proxy: fio::DirectoryProxy) -> Self {
1022 self.pkg_dir_proxy = Some(pkg_dir_proxy);
1023 self
1024 }
1025
1026 pub fn start(mut self, start_on_build: bool) -> Self {
1027 self.start = start_on_build;
1028 self
1029 }
1030
1031 pub fn realm_name(mut self, realm_name: impl Into<String>) -> Self {
1032 self.realm_name = Some(realm_name.into());
1033 self
1034 }
1035}
1036
1037#[derive(Debug)]
1041pub struct RealmBuilder {
1042 root_realm: SubRealmBuilder,
1043 builder_proxy: ftest::BuilderProxy,
1044 component_realm_proxy: fcomponent::RealmProxy,
1045 local_component_runner_builder: LocalComponentRunnerBuilder,
1046 collection_name: String,
1047 start: bool,
1048}
1049
1050impl RealmBuilder {
1051 pub async fn new() -> Result<Self, Error> {
1053 Self::with_params(RealmBuilderParams::new()).await
1054 }
1055
1056 pub async fn with_params(params: RealmBuilderParams) -> Result<Self, Error> {
1057 let component_realm_proxy = match params.component_realm_proxy {
1058 Some(r) => r,
1059 None => fclient::connect_to_protocol::<fcomponent::RealmMarker>()
1060 .map_err(Error::ConnectToServer)?,
1061 };
1062 let pkg_dir_proxy = match params.pkg_dir_proxy {
1063 Some(p) => p,
1064 None => fuchsia_fs::directory::open_in_namespace(
1065 "/pkg",
1066 fuchsia_fs::PERM_READABLE | fuchsia_fs::PERM_EXECUTABLE,
1067 )
1068 .map_err(Error::FailedToOpenPkgDir)?,
1069 };
1070 let collection_name =
1071 params.collection_name.unwrap_or_else(|| DEFAULT_COLLECTION_NAME.into());
1072 let realm_name = params.realm_name.unwrap_or_else(|| {
1073 let id: u64 = rand::random();
1074 format!("auto-{:x}", id)
1075 });
1076 Self::create(
1077 component_realm_proxy,
1078 collection_name,
1079 params.fragment_only_url,
1080 pkg_dir_proxy,
1081 params.start,
1082 realm_name,
1083 )
1084 .await
1085 }
1086
1087 async fn create(
1088 component_realm_proxy: fcomponent::RealmProxy,
1089 collection_name: String,
1090 fragment_only_url: Option<String>,
1091 pkg_dir_proxy: fio::DirectoryProxy,
1092 start: bool,
1093 realm_name: String,
1094 ) -> Result<Self, Error> {
1095 let (exposed_dir_proxy, exposed_dir_server_end) =
1096 endpoints::create_proxy::<fio::DirectoryMarker>();
1097 component_realm_proxy
1098 .open_exposed_dir(
1099 &fdecl::ChildRef {
1100 name: REALM_BUILDER_SERVER_CHILD_NAME.to_string(),
1101 collection: None,
1102 },
1103 exposed_dir_server_end,
1104 )
1105 .await?
1106 .map_err(|e| {
1107 Error::ConnectToServer(format_err!("failed to open exposed dir: {:?}", e))
1108 })?;
1109 let realm_builder_factory_proxy = fclient::connect_to_protocol_at_dir_root::<
1110 ftest::RealmBuilderFactoryMarker,
1111 >(&exposed_dir_proxy)
1112 .map_err(Error::ConnectToServer)?;
1113
1114 let (realm_proxy, realm_server_end) = create_proxy::<ftest::RealmMarker>();
1115 let (builder_proxy, builder_server_end) = create_proxy::<ftest::BuilderMarker>();
1116 match fragment_only_url {
1117 Some(fragment_only_url) => {
1118 realm_builder_factory_proxy
1119 .create_from_relative_url(
1120 ClientEnd::from(pkg_dir_proxy.into_channel().unwrap().into_zx_channel()),
1121 &fragment_only_url,
1122 realm_server_end,
1123 builder_server_end,
1124 )
1125 .await??;
1126 }
1127 None => {
1128 realm_builder_factory_proxy
1129 .create(
1130 ClientEnd::from(pkg_dir_proxy.into_channel().unwrap().into_zx_channel()),
1131 realm_server_end,
1132 builder_server_end,
1133 )
1134 .await??;
1135 }
1136 }
1137 Self::build_struct(
1138 component_realm_proxy,
1139 realm_proxy,
1140 builder_proxy,
1141 collection_name,
1142 start,
1143 realm_name,
1144 )
1145 }
1146
1147 #[allow(clippy::result_large_err)] fn build_struct(
1149 component_realm_proxy: fcomponent::RealmProxy,
1150 realm_proxy: ftest::RealmProxy,
1151 builder_proxy: ftest::BuilderProxy,
1152 collection_name: String,
1153 start: bool,
1154 realm_name: String,
1155 ) -> Result<Self, Error> {
1156 let local_component_runner_builder = LocalComponentRunnerBuilder::new();
1157 Ok(Self {
1158 root_realm: SubRealmBuilder {
1159 realm_proxy,
1160 realm_path: vec![],
1161 local_component_runner_builder: local_component_runner_builder.clone(),
1162 name: realm_name,
1163 },
1164 component_realm_proxy,
1165 builder_proxy,
1166 local_component_runner_builder,
1167 collection_name,
1168 start,
1169 })
1170 }
1171
1172 pub async fn initialize(self) -> Result<(String, fasync::Task<()>), Error> {
1177 let (component_runner_client_end, local_component_runner_task) =
1178 self.local_component_runner_builder.build().await?;
1179 let root_url = self.builder_proxy.build(component_runner_client_end).await??;
1180 Ok((root_url, local_component_runner_task))
1181 }
1182
1183 pub async fn build(self) -> Result<RealmInstance, Error> {
1190 let (component_runner_client_end, local_component_runner_task) =
1191 self.local_component_runner_builder.build().await?;
1192 let root_url = self.builder_proxy.build(component_runner_client_end).await??;
1193 let factory = ScopedInstanceFactory::new(self.collection_name)
1194 .with_realm_proxy(self.component_realm_proxy);
1195 let root = factory
1196 .new_named_instance(self.root_realm.name, root_url)
1197 .await
1198 .map_err(Error::FailedToCreateChild)?;
1199 let realm =
1200 RealmInstance { root, local_component_runner_task: Some(local_component_runner_task) };
1201 if self.start {
1202 realm.root.connect_to_binder().map_err(Error::FailedToBind)?;
1203 }
1204 Ok(realm)
1205 }
1206
1207 pub async fn with_nested_component_manager(
1218 self,
1219 component_manager_fragment_only_url: &str,
1220 ) -> Result<Self, Error> {
1221 self.root_realm.with_nested_component_manager(component_manager_fragment_only_url).await?;
1222 Ok(self)
1223 }
1224
1225 pub async fn build_in_nested_component_manager(
1234 self,
1235 component_manager_fragment_only_url: &str,
1236 ) -> Result<RealmInstance, Error> {
1237 let component_manager_realm =
1238 self.with_nested_component_manager(component_manager_fragment_only_url).await?;
1239 let cm_instance = component_manager_realm.build().await?;
1240 Ok(cm_instance)
1241 }
1242
1243 pub async fn add_child_realm(
1250 &self,
1251 name: impl Into<String>,
1252 options: ChildOptions,
1253 ) -> Result<SubRealmBuilder, Error> {
1254 self.root_realm.add_child_realm(name, options).await
1255 }
1256
1257 pub async fn add_child_realm_from_relative_url(
1258 &self,
1259 name: impl Into<String>,
1260 relative_url: impl Into<String>,
1261 options: ChildOptions,
1262 ) -> Result<SubRealmBuilder, Error> {
1263 self.root_realm.add_child_realm_from_relative_url(name, relative_url, options).await
1264 }
1265
1266 #[cfg(fuchsia_api_level_at_least = "26")]
1267 pub async fn add_child_realm_from_decl(
1268 &self,
1269 name: impl Into<String>,
1270 decl: cm_rust::ComponentDecl,
1271 options: ChildOptions,
1272 ) -> Result<SubRealmBuilder, Error> {
1273 self.root_realm.add_child_realm_from_decl(name, decl, options).await
1274 }
1275
1276 pub async fn add_local_child(
1278 &self,
1279 name: impl Into<String>,
1280 local_component_implementation: impl Fn(
1281 LocalComponentHandles,
1282 )
1283 -> BoxFuture<'static, Result<(), anyhow::Error>>
1284 + Sync
1285 + Send
1286 + 'static,
1287 options: ChildOptions,
1288 ) -> Result<ChildRef, Error> {
1289 self.root_realm.add_local_child(name, local_component_implementation, options).await
1290 }
1291
1292 pub async fn add_child(
1294 &self,
1295 name: impl Into<String>,
1296 url: impl Into<String>,
1297 options: ChildOptions,
1298 ) -> Result<ChildRef, Error> {
1299 self.root_realm.add_child(name, url, options).await
1300 }
1301
1302 pub async fn add_child_from_decl(
1304 &self,
1305 name: impl Into<String>,
1306 decl: cm_rust::ComponentDecl,
1307 options: ChildOptions,
1308 ) -> Result<ChildRef, Error> {
1309 self.root_realm.add_child_from_decl(name, decl, options).await
1310 }
1311
1312 pub async fn get_component_decl(
1323 &self,
1324 name: impl Into<ChildRef>,
1325 ) -> Result<cm_rust::ComponentDecl, Error> {
1326 self.root_realm.get_component_decl(name).await
1327 }
1328
1329 pub async fn replace_component_decl(
1340 &self,
1341 name: impl Into<ChildRef>,
1342 decl: cm_rust::ComponentDecl,
1343 ) -> Result<(), Error> {
1344 self.root_realm.replace_component_decl(name, decl).await
1345 }
1346
1347 pub async fn get_realm_decl(&self) -> Result<cm_rust::ComponentDecl, Error> {
1349 self.root_realm.get_realm_decl().await
1350 }
1351
1352 pub async fn replace_realm_decl(&self, decl: cm_rust::ComponentDecl) -> Result<(), Error> {
1354 self.root_realm.replace_realm_decl(decl).await
1355 }
1356
1357 pub async fn add_route(&self, route: Route) -> Result<(), Error> {
1359 self.root_realm.add_route(route).await
1360 }
1361
1362 pub async fn init_mutable_config_from_package(
1364 &self,
1365 name: impl Into<ChildRef>,
1366 ) -> Result<(), Error> {
1367 self.root_realm.init_mutable_config_from_package(name).await
1368 }
1369
1370 pub async fn init_mutable_config_to_empty(
1372 &self,
1373 name: impl Into<ChildRef>,
1374 ) -> Result<(), Error> {
1375 self.root_realm.init_mutable_config_to_empty(name).await
1376 }
1377
1378 pub async fn set_config_value(
1380 &self,
1381 name: impl Into<ChildRef>,
1382 key: &str,
1383 value: cm_rust::ConfigValue,
1384 ) -> Result<(), Error> {
1385 self.root_realm.set_config_value(name, key, value).await
1386 }
1387
1388 pub async fn read_only_directory(
1392 &self,
1393 directory_name: impl Into<String>,
1394 to: Vec<impl Into<Ref>>,
1395 directory_contents: DirectoryContents,
1396 ) -> Result<(), Error> {
1397 self.root_realm.read_only_directory(directory_name, to, directory_contents).await
1398 }
1399
1400 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1404 pub async fn add_storage(
1405 &self,
1406 storage_name: impl Into<String>,
1407 to: Vec<impl Into<Ref>>,
1408 storage_admin: Option<ServerEnd<fcomponent::StorageAdminMarker>>,
1409 ) -> Result<(), Error> {
1410 self.root_realm.add_storage(storage_name, to, storage_admin).await
1411 }
1412
1413 pub async fn add_capability(&self, capability: cm_rust::CapabilityDecl) -> Result<(), Error> {
1415 self.root_realm.add_capability(capability).await
1416 }
1417
1418 pub async fn add_collection(
1420 &self,
1421 collection: cm_rust::CollectionDecl,
1422 ) -> Result<CollectionRef, Error> {
1423 self.root_realm.add_collection(collection).await
1424 }
1425
1426 pub async fn add_environment(
1428 &self,
1429 environment: cm_rust::EnvironmentDecl,
1430 ) -> Result<(), Error> {
1431 self.root_realm.add_environment(environment).await
1432 }
1433}
1434
1435#[derive(Debug, Clone)]
1436pub struct SubRealmBuilder {
1437 realm_proxy: ftest::RealmProxy,
1438 realm_path: Vec<String>,
1439 local_component_runner_builder: LocalComponentRunnerBuilder,
1440 name: String,
1441}
1442
1443impl SubRealmBuilder {
1444 pub async fn add_child_realm(
1445 &self,
1446 name: impl Into<String>,
1447 options: ChildOptions,
1448 ) -> Result<Self, Error> {
1449 let name: String = name.into();
1450 let (child_realm_proxy, child_realm_server_end) = create_proxy::<ftest::RealmMarker>();
1451 self.realm_proxy.add_child_realm(&name, &options.into(), child_realm_server_end).await??;
1452
1453 let mut child_path = self.realm_path.clone();
1454 child_path.push(name.clone());
1455 Ok(SubRealmBuilder {
1456 realm_proxy: child_realm_proxy,
1457 realm_path: child_path,
1458 local_component_runner_builder: self.local_component_runner_builder.clone(),
1459 name,
1460 })
1461 }
1462
1463 pub async fn add_child_realm_from_relative_url(
1464 &self,
1465 name: impl Into<String>,
1466 relative_url: impl Into<String>,
1467 options: ChildOptions,
1468 ) -> Result<SubRealmBuilder, Error> {
1469 let name: String = name.into();
1470 let (child_realm_proxy, child_realm_server_end) = create_proxy::<ftest::RealmMarker>();
1471 self.realm_proxy
1472 .add_child_realm_from_relative_url(
1473 &name,
1474 &relative_url.into(),
1475 &options.into(),
1476 child_realm_server_end,
1477 )
1478 .await??;
1479
1480 let mut child_path = self.realm_path.clone();
1481 child_path.push(name.clone());
1482 Ok(SubRealmBuilder {
1483 realm_proxy: child_realm_proxy,
1484 realm_path: child_path,
1485 local_component_runner_builder: self.local_component_runner_builder.clone(),
1486 name,
1487 })
1488 }
1489
1490 #[cfg(fuchsia_api_level_at_least = "26")]
1491 pub async fn add_child_realm_from_decl(
1492 &self,
1493 name: impl Into<String>,
1494 decl: cm_rust::ComponentDecl,
1495 options: ChildOptions,
1496 ) -> Result<SubRealmBuilder, Error> {
1497 let name: String = name.into();
1498 let (child_realm_proxy, child_realm_server_end) = create_proxy::<ftest::RealmMarker>();
1499 self.realm_proxy
1500 .add_child_realm_from_decl(
1501 &name,
1502 &decl.native_into_fidl(),
1503 &options.into(),
1504 child_realm_server_end,
1505 )
1506 .await??;
1507
1508 let mut child_path = self.realm_path.clone();
1509 child_path.push(name.clone());
1510 Ok(SubRealmBuilder {
1511 realm_proxy: child_realm_proxy,
1512 realm_path: child_path,
1513 local_component_runner_builder: self.local_component_runner_builder.clone(),
1514 name,
1515 })
1516 }
1517
1518 pub async fn add_local_child<M>(
1520 &self,
1521 name: impl Into<String>,
1522 local_component_implementation: M,
1523 options: ChildOptions,
1524 ) -> Result<ChildRef, Error>
1525 where
1526 M: Fn(LocalComponentHandles) -> BoxFuture<'static, Result<(), anyhow::Error>>
1527 + Sync
1528 + Send
1529 + 'static,
1530 {
1531 let name: String = name.into();
1532 self.realm_proxy.add_local_child(&name, &options.into()).await??;
1533
1534 let mut child_path = self.realm_path.clone();
1535 child_path.push(name.clone());
1536 self.local_component_runner_builder
1537 .register_local_component(child_path.join("/"), local_component_implementation)
1538 .await?;
1539
1540 Ok(ChildRef::new(name, self.realm_path.clone()))
1541 }
1542
1543 pub async fn add_child(
1545 &self,
1546 name: impl Into<String>,
1547 url: impl Into<String>,
1548 options: ChildOptions,
1549 ) -> Result<ChildRef, Error> {
1550 let name: String = name.into();
1551 self.realm_proxy.add_child(&name, &url.into(), &options.into()).await??;
1552 Ok(ChildRef::new(name, self.realm_path.clone()))
1553 }
1554
1555 pub async fn add_child_from_decl(
1557 &self,
1558 name: impl Into<String>,
1559 decl: cm_rust::ComponentDecl,
1560 options: ChildOptions,
1561 ) -> Result<ChildRef, Error> {
1562 let name: String = name.into();
1563 self.realm_proxy
1564 .add_child_from_decl(&name, &decl.native_into_fidl(), &options.into())
1565 .await??;
1566 Ok(ChildRef::new(name, self.realm_path.clone()))
1567 }
1568
1569 pub async fn get_component_decl(
1571 &self,
1572 child_ref: impl Into<ChildRef>,
1573 ) -> Result<cm_rust::ComponentDecl, Error> {
1574 let child_ref: ChildRef = child_ref.into();
1575 child_ref.check_scope(&self.realm_path)?;
1576 let decl = self.realm_proxy.get_component_decl(&child_ref.name).await??;
1577 Ok(decl.fidl_into_native())
1578 }
1579
1580 pub async fn replace_component_decl(
1582 &self,
1583 child_ref: impl Into<ChildRef>,
1584 decl: cm_rust::ComponentDecl,
1585 ) -> Result<(), Error> {
1586 let child_ref: ChildRef = child_ref.into();
1587 child_ref.check_scope(&self.realm_path)?;
1588 self.realm_proxy
1589 .replace_component_decl(&child_ref.name, &decl.native_into_fidl())
1590 .await??;
1591 Ok(())
1592 }
1593
1594 pub async fn get_realm_decl(&self) -> Result<cm_rust::ComponentDecl, Error> {
1596 Ok(self.realm_proxy.get_realm_decl().await??.fidl_into_native())
1597 }
1598
1599 pub async fn replace_realm_decl(&self, decl: cm_rust::ComponentDecl) -> Result<(), Error> {
1601 self.realm_proxy.replace_realm_decl(&decl.native_into_fidl()).await?.map_err(Into::into)
1602 }
1603
1604 pub async fn init_mutable_config_from_package(
1606 &self,
1607 child_ref: impl Into<ChildRef>,
1608 ) -> Result<(), Error> {
1609 let child_ref = child_ref.into();
1610 child_ref.check_scope(&self.realm_path)?;
1611 self.realm_proxy
1612 .init_mutable_config_from_package(&child_ref.name)
1613 .await?
1614 .map_err(Into::into)
1615 }
1616
1617 pub async fn init_mutable_config_to_empty(
1619 &self,
1620 child_ref: impl Into<ChildRef>,
1621 ) -> Result<(), Error> {
1622 let child_ref = child_ref.into();
1623 child_ref.check_scope(&self.realm_path)?;
1624 self.realm_proxy.init_mutable_config_to_empty(&child_ref.name).await?.map_err(Into::into)
1625 }
1626
1627 pub async fn set_config_value(
1629 &self,
1630 child_ref: impl Into<ChildRef>,
1631 key: &str,
1632 value: cm_rust::ConfigValue,
1633 ) -> Result<(), Error> {
1634 let child_ref: ChildRef = child_ref.into();
1635 child_ref.check_scope(&self.realm_path)?;
1636 self.realm_proxy
1637 .set_config_value(
1638 &child_ref.name,
1639 key,
1640 &cm_rust::ConfigValueSpec { value }.native_into_fidl(),
1641 )
1642 .await?
1643 .map_err(Into::into)
1644 }
1645
1646 pub async fn add_route(&self, route: Route) -> Result<(), Error> {
1648 let from = route.from.ok_or(Error::MissingSource)?;
1649
1650 #[allow(unused_mut)] let mut capabilities = route.capabilities;
1652 for target in &route.to {
1653 target.check_scope(&self.realm_path)?;
1654 }
1655 from.check_scope(&self.realm_path)?;
1656 let (route_source, from_dictionary) = from.into_fidl(RefContext::Source);
1657 if !capabilities.is_empty() {
1658 let route_targets = route
1659 .to
1660 .into_iter()
1661 .map(|to| {
1662 let (to, path) = to.into_fidl(RefContext::Target);
1663 assert_matches!(path, None);
1664 to
1665 })
1666 .collect::<Vec<fdecl::Ref>>();
1667 #[cfg(fuchsia_api_level_at_least = "25")]
1672 let fut = self.realm_proxy.add_route_from_dictionary(
1673 &capabilities,
1674 &route_source,
1675 from_dictionary.as_ref().map(|s| s.as_str()).unwrap_or("."),
1676 &route_targets,
1677 );
1678 #[cfg(not(fuchsia_api_level_at_least = "25"))]
1679 let fut = self.realm_proxy.add_route(&capabilities, &route_source, &route_targets);
1680 fut.await??;
1681 }
1682 Ok(())
1683 }
1684
1685 pub async fn read_only_directory(
1689 &self,
1690 directory_name: impl Into<String>,
1691 to: Vec<impl Into<Ref>>,
1692 directory_contents: DirectoryContents,
1693 ) -> Result<(), Error> {
1694 let to: Vec<Ref> = to.into_iter().map(|t| t.into()).collect();
1695 for target in &to {
1696 target.check_scope(&self.realm_path)?;
1697 }
1698 let to = to
1699 .into_iter()
1700 .map(|to| {
1701 let (to, path) = to.into_fidl(RefContext::Target);
1702 assert_matches!(path, None);
1703 to
1704 })
1705 .collect::<Vec<_>>();
1706
1707 let fut = self.realm_proxy.read_only_directory(
1708 &directory_name.into(),
1709 &to,
1710 directory_contents.into(),
1711 );
1712 fut.await??;
1713 Ok(())
1714 }
1715
1716 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1717 pub async fn add_storage(
1718 &self,
1719 storage_name: impl Into<String>,
1720 to: Vec<impl Into<Ref>>,
1721 storage_admin: Option<ServerEnd<fcomponent::StorageAdminMarker>>,
1722 ) -> Result<(), Error> {
1723 let to: Vec<Ref> = to.into_iter().map(Into::into).collect();
1724 for target in &to {
1725 target.check_scope(&self.realm_path)?;
1726 }
1727 let to = to
1728 .into_iter()
1729 .map(|to| {
1730 let (to, path) = to.into_fidl(RefContext::Target);
1731 assert_matches!(path, None);
1732 to
1733 })
1734 .collect::<Vec<_>>();
1735
1736 let fut = self.realm_proxy.add_storage(&storage_name.into(), &to, storage_admin);
1737 fut.await??;
1738 Ok(())
1739 }
1740
1741 pub async fn add_capability(&self, capability: cm_rust::CapabilityDecl) -> Result<(), Error> {
1743 self.realm_proxy.add_capability(&capability.native_into_fidl()).await??;
1744 Ok(())
1745 }
1746
1747 pub async fn add_collection(
1749 &self,
1750 collection: cm_rust::CollectionDecl,
1751 ) -> Result<CollectionRef, Error> {
1752 let name = collection.name.clone().into();
1753 self.realm_proxy.add_collection(&collection.native_into_fidl()).await??;
1754 Ok(CollectionRef::new(name, self.realm_path.clone()))
1755 }
1756
1757 pub async fn add_environment(
1759 &self,
1760 environment: cm_rust::EnvironmentDecl,
1761 ) -> Result<(), Error> {
1762 self.realm_proxy.add_environment(&environment.native_into_fidl()).await??;
1763 Ok(())
1764 }
1765
1766 pub async fn with_nested_component_manager(
1776 &self,
1777 component_manager_fragment_only_url: &str,
1778 ) -> Result<(), Error> {
1779 self.realm_proxy
1780 .use_nested_component_manager(component_manager_fragment_only_url)
1781 .await?
1782 .map_err(Into::into)
1783 }
1784}
1785
1786impl Display for SubRealmBuilder {
1787 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1788 write!(f, "#{}", self.name)
1789 }
1790}
1791
1792pub struct DirectoryContents {
1795 contents: HashMap<String, fmem::Buffer>,
1796}
1797
1798impl DirectoryContents {
1799 pub fn new() -> Self {
1800 Self { contents: HashMap::new() }
1801 }
1802
1803 pub fn add_file(mut self, path: impl Into<String>, contents: impl Into<Vec<u8>>) -> Self {
1804 let contents: Vec<u8> = contents.into();
1805 let vmo = zx::Vmo::create(4096).expect("failed to create a VMO");
1806 vmo.write(&contents, 0).expect("failed to write to VMO");
1807 let buffer = fmem::Buffer { vmo, size: contents.len() as u64 };
1808 self.contents.insert(path.into(), buffer);
1809 self
1810 }
1811}
1812
1813impl Clone for DirectoryContents {
1814 fn clone(&self) -> Self {
1815 let mut new_self = Self::new();
1816 for (path, buf) in self.contents.iter() {
1817 new_self.contents.insert(
1818 path.clone(),
1819 fmem::Buffer {
1820 vmo: buf
1821 .vmo
1822 .create_child(zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE, 0, buf.size)
1823 .expect("failed to clone VMO"),
1824 size: buf.size,
1825 },
1826 );
1827 }
1828 new_self
1829 }
1830}
1831
1832impl From<DirectoryContents> for ftest::DirectoryContents {
1833 fn from(input: DirectoryContents) -> ftest::DirectoryContents {
1834 ftest::DirectoryContents {
1835 entries: input
1836 .contents
1837 .into_iter()
1838 .map(|(path, buf)| ftest::DirectoryEntry { file_path: path, file_contents: buf })
1839 .collect(),
1840 }
1841 }
1842}
1843
1844#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1847pub struct EventStream {
1848 name: String,
1849 scope: Option<Vec<Ref>>,
1850 rename: Option<String>,
1851 path: Option<String>,
1852}
1853
1854impl EventStream {
1855 pub fn new(name: impl Into<String>) -> Self {
1857 Self { name: name.into(), scope: None, rename: None, path: None }
1858 }
1859
1860 pub fn with_scope(mut self, scope: impl Into<Ref>) -> Self {
1863 self.scope.get_or_insert(vec![]).push(scope.into());
1864 self
1865 }
1866
1867 pub fn path(mut self, path: impl Into<String>) -> Self {
1872 self.path = Some(path.into());
1873 self
1874 }
1875
1876 pub fn as_(mut self, name: impl Into<String>) -> Self {
1878 self.rename = Some(name.into());
1879 self
1880 }
1881}
1882
1883#[derive(Debug, Clone)]
1885pub struct ChildOptions {
1886 startup: fdecl::StartupMode,
1887 environment: Option<String>,
1888 on_terminate: fdecl::OnTerminate,
1889 config_overrides: Option<Vec<fdecl::ConfigOverride>>,
1890}
1891
1892impl ChildOptions {
1893 pub fn new() -> Self {
1894 Self {
1895 startup: fdecl::StartupMode::Lazy,
1896 environment: None,
1897 on_terminate: fdecl::OnTerminate::None,
1898 config_overrides: None,
1899 }
1900 }
1901
1902 pub fn eager(mut self) -> Self {
1903 self.startup = fdecl::StartupMode::Eager;
1904 self
1905 }
1906
1907 pub fn environment(mut self, environment: impl Into<String>) -> Self {
1908 self.environment = Some(environment.into());
1909 self
1910 }
1911
1912 pub fn reboot_on_terminate(mut self) -> Self {
1913 self.on_terminate = fdecl::OnTerminate::Reboot;
1914 self
1915 }
1916
1917 pub fn config_overrides(
1918 mut self,
1919 config_overrides: impl Into<Vec<fdecl::ConfigOverride>>,
1920 ) -> Self {
1921 self.config_overrides = Some(config_overrides.into());
1922 self
1923 }
1924}
1925
1926impl Into<ftest::ChildOptions> for ChildOptions {
1927 fn into(self) -> ftest::ChildOptions {
1928 ftest::ChildOptions {
1929 startup: Some(self.startup),
1930 environment: self.environment,
1931 on_terminate: Some(self.on_terminate),
1932 config_overrides: self.config_overrides,
1933 ..Default::default()
1934 }
1935 }
1936}
1937
1938pub struct ScopedInstanceFactory {
1940 realm_proxy: Option<fcomponent::RealmProxy>,
1941 collection_name: String,
1942}
1943
1944impl ScopedInstanceFactory {
1945 pub fn new(collection_name: impl Into<String>) -> Self {
1947 ScopedInstanceFactory { realm_proxy: None, collection_name: collection_name.into() }
1948 }
1949
1950 pub fn with_realm_proxy(mut self, realm_proxy: fcomponent::RealmProxy) -> Self {
1954 self.realm_proxy = Some(realm_proxy);
1955 self
1956 }
1957
1958 pub async fn new_instance(
1961 &self,
1962 url: impl Into<String>,
1963 ) -> Result<ScopedInstance, anyhow::Error> {
1964 let id: u64 = rand::random();
1965 let child_name = format!("auto-{:x}", id);
1966 self.new_named_instance(child_name, url).await
1967 }
1968
1969 pub async fn new_named_instance(
1979 &self,
1980 child_name: impl Into<String>,
1981 url: impl Into<String>,
1982 ) -> Result<ScopedInstance, anyhow::Error> {
1983 let realm = if let Some(realm_proxy) = self.realm_proxy.as_ref() {
1984 realm_proxy.clone()
1985 } else {
1986 fclient::realm().context("Failed to connect to Realm service")?
1987 };
1988 let child_name = child_name.into();
1989 let collection_ref = fdecl::CollectionRef { name: self.collection_name.clone() };
1990 let child_decl = fdecl::Child {
1991 name: Some(child_name.clone()),
1992 url: Some(url.into()),
1993 startup: Some(fdecl::StartupMode::Lazy),
1994 ..Default::default()
1995 };
1996 let (controller_proxy, controller) = create_proxy::<fcomponent::ControllerMarker>();
1997 let child_args = fcomponent::CreateChildArgs {
1998 numbered_handles: None,
1999 controller: Some(controller),
2000 ..Default::default()
2001 };
2002 let () = realm
2003 .create_child(&collection_ref, &child_decl, child_args)
2004 .await
2005 .context("CreateChild FIDL failed.")?
2006 .map_err(|e| format_err!("Failed to create child: {:?}", e))?;
2007 let child_ref = fdecl::ChildRef {
2008 name: child_name.clone(),
2009 collection: Some(self.collection_name.clone()),
2010 };
2011 let (exposed_dir, server) = endpoints::create_proxy::<fio::DirectoryMarker>();
2012 let () = realm
2013 .open_exposed_dir(&child_ref, server)
2014 .await
2015 .context("OpenExposedDir FIDL failed.")?
2016 .map_err(|e|
2017 format_err!("Failed to open exposed dir of child: {:?}", e))?;
2021 Ok(ScopedInstance {
2022 realm,
2023 child_name,
2024 collection: self.collection_name.clone(),
2025 exposed_dir,
2026 destroy_channel: None,
2027 controller_proxy,
2028 })
2029 }
2030}
2031
2032#[must_use = "Dropping `ScopedInstance` will cause the component instance to be stopped and destroyed."]
2035pub struct ScopedInstance {
2036 realm: fcomponent::RealmProxy,
2037 child_name: String,
2038 collection: String,
2039 exposed_dir: fio::DirectoryProxy,
2040 destroy_channel: Option<
2041 futures::channel::oneshot::Sender<
2042 Result<
2043 fidl::client::QueryResponseFut<fcomponent::RealmDestroyChildResult>,
2044 anyhow::Error,
2045 >,
2046 >,
2047 >,
2048 controller_proxy: fcomponent::ControllerProxy,
2049}
2050
2051impl ScopedInstance {
2052 pub async fn new(coll: String, url: String) -> Result<Self, anyhow::Error> {
2055 ScopedInstanceFactory::new(coll).new_instance(url).await
2056 }
2057
2058 pub async fn new_with_name(
2064 child_name: String,
2065 collection: String,
2066 url: String,
2067 ) -> Result<Self, anyhow::Error> {
2068 ScopedInstanceFactory::new(collection).new_named_instance(child_name, url).await
2069 }
2070
2071 pub async fn is_started(&self) -> Result<bool, anyhow::Error> {
2073 Ok(self
2074 .controller_proxy
2075 .is_started()
2076 .await
2077 .context("failed to use controller proxy")?
2078 .map_err(|e| format_err!("failed to determine if component is started: {:?}", e))?)
2079 }
2080
2081 pub async fn start(&self) -> Result<ExecutionController, anyhow::Error> {
2083 self.start_with_args(fcomponent::StartChildArgs::default()).await
2084 }
2085
2086 pub async fn start_with_args(
2089 &self,
2090 args: fcomponent::StartChildArgs,
2091 ) -> Result<ExecutionController, anyhow::Error> {
2092 let (execution_proxy, execution_server_end) =
2093 create_proxy::<fcomponent::ExecutionControllerMarker>();
2094 self.controller_proxy
2095 .start(args, execution_server_end)
2096 .await
2097 .context("failed to use controller proxy")?
2098 .map_err(|e| format_err!("failed to start component: {:?}", e))?;
2099 Ok(ExecutionController::new(execution_proxy))
2100 }
2101
2102 pub fn controller(&self) -> &fcomponent::ControllerProxy {
2103 &self.controller_proxy
2104 }
2105
2106 pub fn connect_to_binder(&self) -> Result<fcomponent::BinderProxy, anyhow::Error> {
2111 let binder: fcomponent::BinderProxy = self
2112 .connect_to_protocol_at_exposed_dir()
2113 .context("failed to connect to fuchsia.component.Binder")?;
2114
2115 Ok(binder)
2116 }
2117
2118 pub async fn start_with_binder_sync(&self) -> Result<(), anyhow::Error> {
2126 let mut event_stream = component_events::events::EventStream::open()
2127 .await
2128 .context("failed to create EventSource")?;
2129
2130 let _: ClientEnd<fcomponent::BinderMarker> = self
2131 .connect_to_protocol_at_exposed_dir()
2132 .context("failed to connect to fuchsia.component.Binder")?;
2133
2134 let _ = EventMatcher::ok()
2135 .moniker(self.moniker())
2136 .wait::<Started>(&mut event_stream)
2137 .await
2138 .context("failed to observe Started event")?;
2139
2140 Ok(())
2141 }
2142
2143 pub fn connect_to_protocol_at_exposed_dir<T: fclient::Connect>(
2145 &self,
2146 ) -> Result<T, anyhow::Error> {
2147 T::connect_at_dir_root(&self.exposed_dir)
2148 }
2149
2150 pub fn connect_to_named_protocol_at_exposed_dir<P: DiscoverableProtocolMarker>(
2152 &self,
2153 protocol_name: &str,
2154 ) -> Result<P::Proxy, anyhow::Error> {
2155 fclient::connect_to_named_protocol_at_dir_root::<P>(&self.exposed_dir, protocol_name)
2156 }
2157
2158 pub fn connect_request_to_protocol_at_exposed_dir<P: DiscoverableProtocolMarker>(
2161 &self,
2162 server_end: ServerEnd<P>,
2163 ) -> Result<(), anyhow::Error> {
2164 self.connect_request_to_named_protocol_at_exposed_dir(
2165 P::PROTOCOL_NAME,
2166 server_end.into_channel(),
2167 )
2168 }
2169
2170 pub fn connect_request_to_named_protocol_at_exposed_dir(
2173 &self,
2174 protocol_name: &str,
2175 server_end: zx::Channel,
2176 ) -> Result<(), anyhow::Error> {
2177 self.exposed_dir
2178 .open(protocol_name, fio::Flags::PROTOCOL_SERVICE, &Default::default(), server_end)
2179 .map_err(Into::into)
2180 }
2181
2182 pub fn get_exposed_dir(&self) -> &fio::DirectoryProxy {
2184 &self.exposed_dir
2185 }
2186
2187 pub fn destroy_waiter_taken(&self) -> bool {
2189 self.destroy_channel.is_some()
2190 }
2191
2192 pub fn take_destroy_waiter(
2195 &mut self,
2196 ) -> impl futures::Future<Output = Result<(), anyhow::Error>> {
2197 if self.destroy_channel.is_some() {
2198 panic!("destroy waiter already taken");
2199 }
2200 let (sender, receiver) = futures::channel::oneshot::channel();
2201 self.destroy_channel = Some(sender);
2202 receiver.err_into().and_then(futures::future::ready).and_then(
2203 |fidl_fut: fidl::client::QueryResponseFut<_>| {
2204 fidl_fut.map(|r: Result<Result<(), fidl_fuchsia_component::Error>, fidl::Error>| {
2205 r.context("DestroyChild FIDL error")?
2206 .map_err(|e| format_err!("Failed to destroy child: {:?}", e))
2207 })
2208 },
2209 )
2210 }
2211
2212 pub fn child_name(&self) -> &str {
2214 self.child_name.as_str()
2215 }
2216
2217 pub fn moniker(&self) -> String {
2219 format!("./{}:{}", self.collection, self.child_name)
2220 }
2221}
2222
2223impl Drop for ScopedInstance {
2224 fn drop(&mut self) {
2225 let Self { realm, collection, child_name, destroy_channel, .. } = self;
2226 let child_ref =
2227 fdecl::ChildRef { name: child_name.clone(), collection: Some(collection.clone()) };
2228 let result = Ok(realm.destroy_child(&child_ref));
2234 if let Some(chan) = destroy_channel.take() {
2235 let () = chan.send(result).unwrap_or_else(|result| {
2236 warn!("Failed to send result for destroyed scoped instance. Result={:?}", result);
2237 });
2238 }
2239 }
2240}
2241
2242pub struct ExecutionController {
2247 execution_proxy: fcomponent::ExecutionControllerProxy,
2248 execution_event_stream: fcomponent::ExecutionControllerEventStream,
2249}
2250
2251impl ExecutionController {
2252 fn new(execution_proxy: fcomponent::ExecutionControllerProxy) -> Self {
2253 let execution_event_stream = execution_proxy.take_event_stream();
2254 Self { execution_proxy, execution_event_stream }
2255 }
2256
2257 pub async fn stop(self) -> Result<fcomponent::StoppedPayload, anyhow::Error> {
2260 let _ = self.execution_proxy.stop();
2264 self.wait_for_stop().await
2265 }
2266
2267 pub async fn wait_for_stop(mut self) -> Result<fcomponent::StoppedPayload, anyhow::Error> {
2269 loop {
2270 match self.execution_event_stream.try_next().await {
2271 Ok(Some(fcomponent::ExecutionControllerEvent::OnStop { stopped_payload })) => {
2272 return Ok(stopped_payload);
2273 }
2274 Ok(Some(fcomponent::ExecutionControllerEvent::_UnknownEvent {
2275 ordinal, ..
2276 })) => {
2277 warn!(ordinal:%; "fuchsia.component/ExecutionController delivered unknown event");
2278 }
2279 Ok(None) => {
2280 return Err(format_err!("ExecutionController closed and no OnStop received"));
2281 }
2282 Err(e) => {
2283 return Err(format_err!("failed to wait for OnStop: {:?}", e));
2284 }
2285 }
2286 }
2287 }
2288}
2289
2290#[cfg(test)]
2291mod tests {
2292 use super::*;
2293 use assert_matches::assert_matches;
2294 use fidl::endpoints::create_proxy_and_stream;
2295 use fidl_fuchsia_component as fcomponent;
2296 use futures::channel::mpsc;
2297 use futures::future::pending;
2298 use futures::{SinkExt, StreamExt};
2299
2300 #[fuchsia::test]
2305 fn child_options_to_fidl() {
2306 let options: ftest::ChildOptions = ChildOptions::new().into();
2307 assert_eq!(
2308 options,
2309 ftest::ChildOptions {
2310 startup: Some(fdecl::StartupMode::Lazy),
2312 on_terminate: Some(fdecl::OnTerminate::None),
2313 ..Default::default()
2314 },
2315 );
2316 assert_eq!(
2317 options,
2318 ftest::ChildOptions {
2319 startup: Some(fdecl::StartupMode::Lazy),
2320 environment: None,
2321 on_terminate: Some(fdecl::OnTerminate::None),
2322 config_overrides: None,
2323 __source_breaking: fidl::marker::SourceBreaking,
2324 },
2325 );
2326 let options: ftest::ChildOptions = ChildOptions::new().eager().into();
2327 assert_eq!(
2328 options,
2329 ftest::ChildOptions {
2330 startup: Some(fdecl::StartupMode::Eager),
2331 environment: None,
2332 on_terminate: Some(fdecl::OnTerminate::None),
2333 config_overrides: None,
2334 __source_breaking: fidl::marker::SourceBreaking,
2335 },
2336 );
2337 let options: ftest::ChildOptions = ChildOptions::new().environment("test_env").into();
2338 assert_eq!(
2339 options,
2340 ftest::ChildOptions {
2341 startup: Some(fdecl::StartupMode::Lazy),
2342 environment: Some("test_env".to_string()),
2343 on_terminate: Some(fdecl::OnTerminate::None),
2344 config_overrides: None,
2345 __source_breaking: fidl::marker::SourceBreaking,
2346 },
2347 );
2348 let options: ftest::ChildOptions = ChildOptions::new().reboot_on_terminate().into();
2349 assert_eq!(
2350 options,
2351 ftest::ChildOptions {
2352 startup: Some(fdecl::StartupMode::Lazy),
2353 environment: None,
2354 on_terminate: Some(fdecl::OnTerminate::Reboot),
2355 config_overrides: None,
2356 __source_breaking: fidl::marker::SourceBreaking,
2357 },
2358 );
2359
2360 let mut config_overrides: Vec<fdecl::ConfigOverride> = vec![];
2361 config_overrides.push(fdecl::ConfigOverride {
2362 key: Some("mystring".to_string()),
2363 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::String(
2364 "Fuchsia".to_string(),
2365 ))),
2366 ..Default::default()
2367 });
2368 config_overrides.push(fdecl::ConfigOverride {
2369 key: Some("mynumber".to_string()),
2370 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Uint64(200))),
2371 ..Default::default()
2372 });
2373 let options: ftest::ChildOptions =
2374 ChildOptions::new().config_overrides(config_overrides.clone()).into();
2375 assert_eq!(
2376 options,
2377 ftest::ChildOptions {
2378 startup: Some(fdecl::StartupMode::Lazy),
2379 environment: None,
2380 on_terminate: Some(fdecl::OnTerminate::None),
2381 config_overrides: Some(config_overrides),
2382 __source_breaking: fidl::marker::SourceBreaking,
2383 },
2384 );
2385 }
2386
2387 #[fuchsia::test]
2388 async fn child_scope_prevents_cross_realm_usage() {
2389 let (builder, _server_task, receive_server_requests) = new_realm_builder_and_server_task();
2390 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
2391 let child_realm_b = builder.add_child_realm("b", ChildOptions::new()).await.unwrap();
2392 let child_c = child_realm_b.add_child("c", "test://c", ChildOptions::new()).await.unwrap();
2393
2394 assert_matches!(
2395 builder.add_route(
2396 Route::new()
2397 .capability(Capability::protocol::<fcomponent::RealmMarker>())
2398 .from(&child_a)
2399 .to(&child_c)
2400 ).await,
2401 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_c).into()
2402 );
2403
2404 assert_matches!(
2405 child_realm_b.add_route(
2406 Route::new()
2407 .capability(Capability::protocol::<fcomponent::RealmMarker>())
2408 .from(&child_a)
2409 .to(&child_c)
2410 ).await,
2411 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_a).into()
2412 );
2413
2414 assert_matches!(
2415 builder.get_component_decl(&child_c).await,
2416 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_c).into()
2417 );
2418
2419 assert_matches!(
2420 child_realm_b.get_component_decl(&child_a).await,
2421 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_a).into()
2422 );
2423
2424 assert_matches!(
2425 builder.replace_component_decl(&child_c, cm_rust::ComponentDecl::default()).await,
2426 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_c).into()
2427 );
2428
2429 assert_matches!(
2430 child_realm_b.replace_component_decl(&child_a, cm_rust::ComponentDecl::default()).await,
2431 Err(Error::RefUsedInWrongRealm(ref_, _)) if ref_ == (&child_a).into()
2432 );
2433
2434 let sub_realm_receiver = confirm_num_server_requests(receive_server_requests, 2).remove(0);
2437 confirm_num_server_requests(sub_realm_receiver, 1);
2438 }
2439
2440 #[fuchsia::test]
2441 async fn child_ref_construction() {
2442 let (builder, _server_task, receive_server_requests) = new_realm_builder_and_server_task();
2443 let child_realm_a = builder.add_child_realm("a", ChildOptions::new()).await.unwrap();
2444 let child_realm_b = child_realm_a.add_child_realm("b", ChildOptions::new()).await.unwrap();
2445
2446 let child_ref_a: ChildRef = (&child_realm_a).into();
2447 let child_ref_b: ChildRef = (&child_realm_b).into();
2448
2449 assert_eq!(child_ref_a, ChildRef::new("a".to_string(), vec![]),);
2450
2451 assert_eq!(child_ref_b, ChildRef::new("b".to_string(), vec!["a".to_string()]),);
2452
2453 let child_ref_c = builder.add_child("c", "test://c", ChildOptions::new()).await.unwrap();
2454 let child_ref_d =
2455 child_realm_a.add_child("d", "test://d", ChildOptions::new()).await.unwrap();
2456 let child_ref_e =
2457 child_realm_b.add_child("e", "test://e", ChildOptions::new()).await.unwrap();
2458
2459 assert_eq!(child_ref_c, ChildRef::new("c".to_string(), vec![]),);
2460
2461 assert_eq!(child_ref_d, ChildRef::new("d".to_string(), vec!["a".to_string()]),);
2462
2463 assert_eq!(
2464 child_ref_e,
2465 ChildRef::new("e".to_string(), vec!["a".to_string(), "b".to_string()]),
2466 );
2467
2468 confirm_num_server_requests(receive_server_requests, 2);
2471 }
2472
2473 #[fuchsia::test]
2474 async fn protocol_capability_construction() {
2475 assert_eq!(
2476 Capability::protocol_by_name("test"),
2477 ProtocolCapability {
2478 name: "test".to_string(),
2479 as_: None,
2480 type_: fdecl::DependencyType::Strong,
2481 path: None,
2482 availability: None,
2483 },
2484 );
2485 assert_eq!(
2486 Capability::protocol::<ftest::RealmBuilderFactoryMarker>(),
2487 ProtocolCapability {
2488 name: ftest::RealmBuilderFactoryMarker::PROTOCOL_NAME.to_string(),
2489 as_: None,
2490 type_: fdecl::DependencyType::Strong,
2491 path: None,
2492 availability: None,
2493 },
2494 );
2495 assert_eq!(
2496 Capability::protocol_by_name("test").as_("test2"),
2497 ProtocolCapability {
2498 name: "test".to_string(),
2499 as_: Some("test2".to_string()),
2500 type_: fdecl::DependencyType::Strong,
2501 path: None,
2502 availability: None,
2503 },
2504 );
2505 assert_eq!(
2506 Capability::protocol_by_name("test").weak(),
2507 ProtocolCapability {
2508 name: "test".to_string(),
2509 as_: None,
2510 type_: fdecl::DependencyType::Weak,
2511 path: None,
2512 availability: None,
2513 },
2514 );
2515 assert_eq!(
2516 Capability::protocol_by_name("test").path("/svc/test2"),
2517 ProtocolCapability {
2518 name: "test".to_string(),
2519 as_: None,
2520 type_: fdecl::DependencyType::Strong,
2521 path: Some("/svc/test2".to_string()),
2522 availability: None,
2523 },
2524 );
2525 assert_eq!(
2526 Capability::protocol_by_name("test").optional(),
2527 ProtocolCapability {
2528 name: "test".to_string(),
2529 as_: None,
2530 type_: fdecl::DependencyType::Strong,
2531 path: None,
2532 availability: Some(fdecl::Availability::Optional),
2533 },
2534 );
2535 assert_eq!(
2536 Capability::protocol_by_name("test").availability_same_as_target(),
2537 ProtocolCapability {
2538 name: "test".to_string(),
2539 as_: None,
2540 type_: fdecl::DependencyType::Strong,
2541 path: None,
2542 availability: Some(fdecl::Availability::SameAsTarget),
2543 },
2544 );
2545 }
2546
2547 #[fuchsia::test]
2548 async fn directory_capability_construction() {
2549 assert_eq!(
2550 Capability::directory("test"),
2551 DirectoryCapability {
2552 name: "test".to_string(),
2553 as_: None,
2554 type_: fdecl::DependencyType::Strong,
2555 rights: None,
2556 subdir: None,
2557 path: None,
2558 availability: None,
2559 },
2560 );
2561 assert_eq!(
2562 Capability::directory("test").as_("test2"),
2563 DirectoryCapability {
2564 name: "test".to_string(),
2565 as_: Some("test2".to_string()),
2566 type_: fdecl::DependencyType::Strong,
2567 rights: None,
2568 subdir: None,
2569 path: None,
2570 availability: None,
2571 },
2572 );
2573 assert_eq!(
2574 Capability::directory("test").weak(),
2575 DirectoryCapability {
2576 name: "test".to_string(),
2577 as_: None,
2578 type_: fdecl::DependencyType::Weak,
2579 rights: None,
2580 subdir: None,
2581 path: None,
2582 availability: None,
2583 },
2584 );
2585 assert_eq!(
2586 Capability::directory("test").rights(fio::RX_STAR_DIR),
2587 DirectoryCapability {
2588 name: "test".to_string(),
2589 as_: None,
2590 type_: fdecl::DependencyType::Strong,
2591 rights: Some(fio::RX_STAR_DIR),
2592 subdir: None,
2593 path: None,
2594 availability: None,
2595 },
2596 );
2597 assert_eq!(
2598 Capability::directory("test").subdir("test2"),
2599 DirectoryCapability {
2600 name: "test".to_string(),
2601 as_: None,
2602 type_: fdecl::DependencyType::Strong,
2603 rights: None,
2604 subdir: Some("test2".to_string()),
2605 path: None,
2606 availability: None,
2607 },
2608 );
2609 assert_eq!(
2610 Capability::directory("test").path("/test2"),
2611 DirectoryCapability {
2612 name: "test".to_string(),
2613 as_: None,
2614 type_: fdecl::DependencyType::Strong,
2615 rights: None,
2616 subdir: None,
2617 path: Some("/test2".to_string()),
2618 availability: None,
2619 },
2620 );
2621 assert_eq!(
2622 Capability::directory("test").optional(),
2623 DirectoryCapability {
2624 name: "test".to_string(),
2625 as_: None,
2626 type_: fdecl::DependencyType::Strong,
2627 rights: None,
2628 subdir: None,
2629 path: None,
2630 availability: Some(fdecl::Availability::Optional),
2631 },
2632 );
2633 assert_eq!(
2634 Capability::directory("test").availability_same_as_target(),
2635 DirectoryCapability {
2636 name: "test".to_string(),
2637 as_: None,
2638 type_: fdecl::DependencyType::Strong,
2639 rights: None,
2640 subdir: None,
2641 path: None,
2642 availability: Some(fdecl::Availability::SameAsTarget),
2643 },
2644 );
2645 }
2646
2647 #[fuchsia::test]
2648 async fn storage_capability_construction() {
2649 assert_eq!(
2650 Capability::storage("test"),
2651 StorageCapability {
2652 name: "test".to_string(),
2653 as_: None,
2654 path: None,
2655 availability: None
2656 },
2657 );
2658 assert_eq!(
2659 Capability::storage("test").as_("test2"),
2660 StorageCapability {
2661 name: "test".to_string(),
2662 as_: Some("test2".to_string()),
2663 path: None,
2664 availability: None,
2665 },
2666 );
2667 assert_eq!(
2668 Capability::storage("test").path("/test2"),
2669 StorageCapability {
2670 name: "test".to_string(),
2671 as_: None,
2672 path: Some("/test2".to_string()),
2673 availability: None,
2674 },
2675 );
2676 assert_eq!(
2677 Capability::storage("test").optional(),
2678 StorageCapability {
2679 name: "test".to_string(),
2680 as_: None,
2681 path: None,
2682 availability: Some(fdecl::Availability::Optional),
2683 },
2684 );
2685 assert_eq!(
2686 Capability::storage("test").availability_same_as_target(),
2687 StorageCapability {
2688 name: "test".to_string(),
2689 as_: None,
2690 path: None,
2691 availability: Some(fdecl::Availability::SameAsTarget),
2692 },
2693 );
2694 }
2695
2696 #[fuchsia::test]
2697 async fn service_capability_construction() {
2698 assert_eq!(
2699 Capability::service_by_name("test"),
2700 ServiceCapability {
2701 name: "test".to_string(),
2702 as_: None,
2703 path: None,
2704 availability: None
2705 },
2706 );
2707 assert_eq!(
2708 Capability::service_by_name("test").as_("test2"),
2709 ServiceCapability {
2710 name: "test".to_string(),
2711 as_: Some("test2".to_string()),
2712 path: None,
2713 availability: None,
2714 },
2715 );
2716 assert_eq!(
2717 Capability::service_by_name("test").path("/svc/test2"),
2718 ServiceCapability {
2719 name: "test".to_string(),
2720 as_: None,
2721 path: Some("/svc/test2".to_string()),
2722 availability: None,
2723 },
2724 );
2725 assert_eq!(
2726 Capability::service_by_name("test").optional(),
2727 ServiceCapability {
2728 name: "test".to_string(),
2729 as_: None,
2730 path: None,
2731 availability: Some(fdecl::Availability::Optional),
2732 },
2733 );
2734 assert_eq!(
2735 Capability::service_by_name("test").availability_same_as_target(),
2736 ServiceCapability {
2737 name: "test".to_string(),
2738 as_: None,
2739 path: None,
2740 availability: Some(fdecl::Availability::SameAsTarget),
2741 },
2742 );
2743 }
2744
2745 #[fuchsia::test]
2746 async fn dictionary_capability_construction() {
2747 assert_eq!(
2748 Capability::dictionary("test"),
2749 DictionaryCapability { name: "test".to_string(), as_: None, availability: None },
2750 );
2751 assert_eq!(
2752 Capability::dictionary("test").as_("test2"),
2753 DictionaryCapability {
2754 name: "test".to_string(),
2755 as_: Some("test2".to_string()),
2756 availability: None,
2757 },
2758 );
2759 assert_eq!(
2760 Capability::dictionary("test").optional(),
2761 DictionaryCapability {
2762 name: "test".to_string(),
2763 as_: None,
2764 availability: Some(fdecl::Availability::Optional),
2765 },
2766 );
2767 assert_eq!(
2768 Capability::dictionary("test").availability_same_as_target(),
2769 DictionaryCapability {
2770 name: "test".to_string(),
2771 as_: None,
2772 availability: Some(fdecl::Availability::SameAsTarget),
2773 },
2774 );
2775 }
2776
2777 #[fuchsia::test]
2778 async fn route_construction() {
2779 assert_eq!(
2780 Route::new()
2781 .capability(Capability::protocol_by_name("test"))
2782 .capability(Capability::protocol_by_name("test2"))
2783 .from(Ref::child("a"))
2784 .to(Ref::collection("b"))
2785 .to(Ref::parent()),
2786 Route {
2787 capabilities: vec![
2788 Capability::protocol_by_name("test").into(),
2789 Capability::protocol_by_name("test2").into(),
2790 ],
2791 from: Some(Ref::child("a").into()),
2792 to: vec![Ref::collection("b").into(), Ref::parent().into(),],
2793 },
2794 );
2795 }
2796
2797 #[derive(Debug)]
2798 enum ServerRequest {
2799 AddChild {
2800 name: String,
2801 url: String,
2802 options: ftest::ChildOptions,
2803 },
2804 AddChildFromDecl {
2805 name: String,
2806 decl: fdecl::Component,
2807 options: ftest::ChildOptions,
2808 },
2809 AddLocalChild {
2810 name: String,
2811 options: ftest::ChildOptions,
2812 },
2813 AddChildRealm {
2814 name: String,
2815 options: ftest::ChildOptions,
2816 receive_requests: mpsc::UnboundedReceiver<ServerRequest>,
2817 },
2818 AddChildRealmFromRelativeUrl {
2819 name: String,
2820 relative_url: String,
2821 options: ftest::ChildOptions,
2822 receive_requests: mpsc::UnboundedReceiver<ServerRequest>,
2823 },
2824 AddChildRealmFromDecl {
2825 name: String,
2826 decl: fdecl::Component,
2827 options: ftest::ChildOptions,
2828 receive_requests: mpsc::UnboundedReceiver<ServerRequest>,
2829 },
2830 GetComponentDecl {
2831 name: String,
2832 },
2833 ReplaceComponentDecl {
2834 name: String,
2835 component_decl: fdecl::Component,
2836 },
2837 UseNestedComponentManager {
2838 #[allow(unused)]
2839 component_manager_relative_url: String,
2840 },
2841 GetRealmDecl,
2842 ReplaceRealmDecl {
2843 component_decl: fdecl::Component,
2844 },
2845 AddRouteFromDictionary {
2846 capabilities: Vec<ftest::Capability>,
2847 from: fdecl::Ref,
2848 from_dictionary: String,
2849 to: Vec<fdecl::Ref>,
2850 },
2851 ReadOnlyDirectory {
2852 name: String,
2853 to: Vec<fdecl::Ref>,
2854 },
2855 AddStorage {
2856 name: String,
2857 to: Vec<fdecl::Ref>,
2858 },
2859 InitMutableConfigFromPackage {
2860 name: String,
2861 },
2862 InitMutableConfigToEmpty {
2863 name: String,
2864 },
2865 AddCapability {
2866 capability: fdecl::Capability,
2867 },
2868 AddCollection {
2869 collection: fdecl::Collection,
2870 },
2871 AddEnvironment {
2872 environment: fdecl::Environment,
2873 },
2874 SetConfigValue {
2875 name: String,
2876 key: String,
2877 value: fdecl::ConfigValueSpec,
2878 },
2879 }
2880
2881 fn handle_realm_stream(
2882 mut stream: ftest::RealmRequestStream,
2883 mut report_requests: mpsc::UnboundedSender<ServerRequest>,
2884 ) -> BoxFuture<'static, ()> {
2885 async move {
2886 let mut child_realm_streams = vec![];
2887 while let Some(req) = stream.try_next().await.unwrap() {
2888 match req {
2889 ftest::RealmRequest::AddChild { responder, name, url, options } => {
2890 report_requests
2891 .send(ServerRequest::AddChild { name, url, options })
2892 .await
2893 .unwrap();
2894 responder.send(Ok(())).unwrap();
2895 }
2896 ftest::RealmRequest::AddChildFromDecl { responder, name, decl, options } => {
2897 report_requests
2898 .send(ServerRequest::AddChildFromDecl { name, decl, options })
2899 .await
2900 .unwrap();
2901 responder.send(Ok(())).unwrap();
2902 }
2903 ftest::RealmRequest::AddLocalChild { responder, name, options } => {
2904 report_requests
2905 .send(ServerRequest::AddLocalChild { name, options })
2906 .await
2907 .unwrap();
2908 responder.send(Ok(())).unwrap();
2909 }
2910 ftest::RealmRequest::AddChildRealm {
2911 responder,
2912 child_realm,
2913 name,
2914 options,
2915 } => {
2916 let (child_realm_report_requests, receive_requests) = mpsc::unbounded();
2917
2918 report_requests
2919 .send(ServerRequest::AddChildRealm { name, options, receive_requests })
2920 .await
2921 .unwrap();
2922
2923 let child_realm_stream = child_realm.into_stream();
2924 child_realm_streams.push(fasync::Task::spawn(async move {
2925 handle_realm_stream(child_realm_stream, child_realm_report_requests)
2926 .await
2927 }));
2928 responder.send(Ok(())).unwrap();
2929 }
2930 ftest::RealmRequest::AddChildRealmFromRelativeUrl {
2931 responder,
2932 child_realm,
2933 name,
2934 relative_url,
2935 options,
2936 } => {
2937 let (child_realm_report_requests, receive_requests) = mpsc::unbounded();
2938
2939 report_requests
2940 .send(ServerRequest::AddChildRealmFromRelativeUrl {
2941 name,
2942 relative_url,
2943 options,
2944 receive_requests,
2945 })
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::AddChildRealmFromDecl {
2957 name,
2958 decl,
2959 options,
2960 child_realm,
2961 responder,
2962 } => {
2963 let (child_realm_report_requests, receive_requests) = mpsc::unbounded();
2964
2965 report_requests
2966 .send(ServerRequest::AddChildRealmFromDecl {
2967 name,
2968 decl,
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::GetComponentDecl { responder, name } => {
2983 report_requests
2984 .send(ServerRequest::GetComponentDecl { name })
2985 .await
2986 .unwrap();
2987 responder.send(Ok(&fdecl::Component::default())).unwrap();
2988 }
2989 ftest::RealmRequest::UseNestedComponentManager {
2990 responder,
2991 component_manager_relative_url,
2992 } => {
2993 report_requests
2994 .send(ServerRequest::UseNestedComponentManager {
2995 component_manager_relative_url,
2996 })
2997 .await
2998 .unwrap();
2999 responder.send(Ok(())).unwrap();
3000 }
3001 ftest::RealmRequest::ReplaceComponentDecl {
3002 responder,
3003 name,
3004 component_decl,
3005 } => {
3006 report_requests
3007 .send(ServerRequest::ReplaceComponentDecl { name, component_decl })
3008 .await
3009 .unwrap();
3010 responder.send(Ok(())).unwrap();
3011 }
3012 ftest::RealmRequest::GetRealmDecl { responder } => {
3013 report_requests.send(ServerRequest::GetRealmDecl).await.unwrap();
3014 responder.send(Ok(&fdecl::Component::default())).unwrap();
3015 }
3016 ftest::RealmRequest::ReplaceRealmDecl { responder, component_decl } => {
3017 report_requests
3018 .send(ServerRequest::ReplaceRealmDecl { component_decl })
3019 .await
3020 .unwrap();
3021 responder.send(Ok(())).unwrap();
3022 }
3023 ftest::RealmRequest::AddRoute { .. } => {
3024 panic!("AddRoute is deprecated");
3025 }
3026 ftest::RealmRequest::AddRouteFromDictionary {
3027 responder,
3028 capabilities,
3029 from,
3030 from_dictionary,
3031 to,
3032 } => {
3033 report_requests
3034 .send(ServerRequest::AddRouteFromDictionary {
3035 capabilities,
3036 from,
3037 from_dictionary,
3038 to,
3039 })
3040 .await
3041 .unwrap();
3042 responder.send(Ok(())).unwrap();
3043 }
3044 ftest::RealmRequest::ReadOnlyDirectory { responder, name, to, .. } => {
3045 report_requests
3046 .send(ServerRequest::ReadOnlyDirectory { name, to })
3047 .await
3048 .unwrap();
3049 responder.send(Ok(())).unwrap();
3050 }
3051 ftest::RealmRequest::AddStorage { responder, name, to, .. } => {
3052 report_requests.send(ServerRequest::AddStorage { name, to }).await.unwrap();
3053 responder.send(Ok(())).unwrap();
3054 }
3055 ftest::RealmRequest::InitMutableConfigFromPackage { name, responder } => {
3056 report_requests
3057 .send(ServerRequest::InitMutableConfigFromPackage { name })
3058 .await
3059 .unwrap();
3060 responder.send(Ok(())).unwrap();
3061 }
3062 ftest::RealmRequest::InitMutableConfigToEmpty { name, responder } => {
3063 report_requests
3064 .send(ServerRequest::InitMutableConfigToEmpty { name })
3065 .await
3066 .unwrap();
3067 responder.send(Ok(())).unwrap();
3068 }
3069 ftest::RealmRequest::AddCapability { capability, responder } => {
3070 report_requests
3071 .send(ServerRequest::AddCapability { capability })
3072 .await
3073 .unwrap();
3074 responder.send(Ok(())).unwrap();
3075 }
3076 ftest::RealmRequest::AddCollection { collection, responder } => {
3077 report_requests
3078 .send(ServerRequest::AddCollection { collection })
3079 .await
3080 .unwrap();
3081 responder.send(Ok(())).unwrap();
3082 }
3083 ftest::RealmRequest::AddEnvironment { environment, responder } => {
3084 report_requests
3085 .send(ServerRequest::AddEnvironment { environment })
3086 .await
3087 .unwrap();
3088 responder.send(Ok(())).unwrap();
3089 }
3090 ftest::RealmRequest::SetConfigValue { responder, name, key, value } => {
3091 report_requests
3092 .send(ServerRequest::SetConfigValue { name, key, value })
3093 .await
3094 .unwrap();
3095 responder.send(Ok(())).unwrap();
3096 }
3097 }
3098 }
3099 }
3100 .boxed()
3101 }
3102
3103 fn new_realm_builder_and_server_task()
3104 -> (RealmBuilder, fasync::Task<()>, mpsc::UnboundedReceiver<ServerRequest>) {
3105 let (realm_proxy, realm_stream) = create_proxy_and_stream::<ftest::RealmMarker>();
3106 let (builder_proxy, mut builder_stream) = create_proxy_and_stream::<ftest::BuilderMarker>();
3107
3108 let builder_task = fasync::Task::spawn(async move {
3109 while let Some(req) = builder_stream.try_next().await.unwrap() {
3110 match req {
3111 ftest::BuilderRequest::Build { runner, responder } => {
3112 drop(runner);
3113 responder.send(Ok("test://hippo")).unwrap();
3114 }
3115 }
3116 }
3117 });
3118
3119 let (realm_report_requests, realm_receive_requests) = mpsc::unbounded();
3120 let server_task = fasync::Task::spawn(async move {
3121 let _builder_task = builder_task;
3122 handle_realm_stream(realm_stream, realm_report_requests).await
3123 });
3124 let id: u64 = rand::random();
3125 let realm_name = format!("auto-{:x}", id);
3126 let component_realm_proxy =
3127 fclient::connect_to_protocol::<fcomponent::RealmMarker>().unwrap();
3128
3129 (
3130 RealmBuilder::build_struct(
3131 component_realm_proxy,
3132 realm_proxy,
3133 builder_proxy,
3134 crate::DEFAULT_COLLECTION_NAME.to_string(),
3135 false,
3136 realm_name,
3137 )
3138 .unwrap(),
3139 server_task,
3140 realm_receive_requests,
3141 )
3142 }
3143
3144 fn confirm_num_server_requests(
3147 mut server_requests: mpsc::UnboundedReceiver<ServerRequest>,
3148 num: usize,
3149 ) -> Vec<mpsc::UnboundedReceiver<ServerRequest>> {
3150 let mut discovered_receivers = vec![];
3151 for i in 0..num {
3152 match server_requests.next().now_or_never() {
3153 Some(Some(ServerRequest::AddChildRealm { receive_requests, .. })) => {
3154 discovered_receivers.push(receive_requests)
3155 }
3156 Some(Some(_)) => (),
3157 Some(None) => panic!("server_requests ended unexpectedly"),
3158 None => panic!("server_requests had less messages in it than we expected: {}", i),
3159 }
3160 }
3161 assert_matches!(server_requests.next().now_or_never(), None);
3162 discovered_receivers
3163 }
3164
3165 fn assert_add_child_realm(
3166 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3167 expected_name: &str,
3168 expected_options: ftest::ChildOptions,
3169 ) -> mpsc::UnboundedReceiver<ServerRequest> {
3170 match receive_server_requests.next().now_or_never() {
3171 Some(Some(ServerRequest::AddChildRealm { name, options, receive_requests }))
3172 if &name == expected_name && options == expected_options =>
3173 {
3174 receive_requests
3175 }
3176 req => panic!("match failed, received unexpected server request: {:?}", req),
3177 }
3178 }
3179
3180 fn assert_add_child_realm_from_relative_url(
3181 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3182 expected_name: &str,
3183 expected_relative_url: &str,
3184 expected_options: ftest::ChildOptions,
3185 ) -> mpsc::UnboundedReceiver<ServerRequest> {
3186 match receive_server_requests.next().now_or_never() {
3187 Some(Some(ServerRequest::AddChildRealmFromRelativeUrl {
3188 name,
3189 relative_url,
3190 options,
3191 receive_requests,
3192 })) if &name == expected_name
3193 && options == expected_options
3194 && relative_url == expected_relative_url =>
3195 {
3196 receive_requests
3197 }
3198 req => panic!("match failed, received unexpected server request: {:?}", req),
3199 }
3200 }
3201
3202 fn assert_add_child_realm_from_decl(
3203 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3204 expected_name: &str,
3205 expected_decl: &fdecl::Component,
3206 expected_options: ftest::ChildOptions,
3207 ) -> mpsc::UnboundedReceiver<ServerRequest> {
3208 match receive_server_requests.next().now_or_never() {
3209 Some(Some(ServerRequest::AddChildRealmFromDecl {
3210 name,
3211 decl,
3212 options,
3213 receive_requests,
3214 })) if &name == expected_name
3215 && options == expected_options
3216 && decl == *expected_decl =>
3217 {
3218 receive_requests
3219 }
3220 req => panic!("match failed, received unexpected server request: {:?}", req),
3221 }
3222 }
3223
3224 fn assert_read_only_directory(
3225 receive_server_requests: &mut mpsc::UnboundedReceiver<ServerRequest>,
3226 expected_directory_name: &str,
3227 expected_targets: Vec<impl Into<Ref>>,
3228 ) {
3229 let expected_targets = expected_targets
3230 .into_iter()
3231 .map(|t| {
3232 let t: Ref = t.into();
3233 t.into_fidl(RefContext::Target).0
3234 })
3235 .collect::<Vec<_>>();
3236
3237 match receive_server_requests.next().now_or_never() {
3238 Some(Some(ServerRequest::ReadOnlyDirectory { name, to, .. }))
3239 if &name == expected_directory_name && to == expected_targets =>
3240 {
3241 return;
3242 }
3243 req => panic!("match failed, received unexpected server request: {:?}", req),
3244 }
3245 }
3246
3247 #[fuchsia::test]
3248 async fn add_child() {
3249 let (builder, _server_task, mut receive_server_requests) =
3250 new_realm_builder_and_server_task();
3251 let _child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3252 assert_matches!(
3253 receive_server_requests.next().await,
3254 Some(ServerRequest::AddChild { name, url, options })
3255 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3256 );
3257 assert_matches!(receive_server_requests.next().now_or_never(), None);
3258 }
3259
3260 #[fuchsia::test]
3261 async fn add_child_from_decl() {
3262 let (builder, _server_task, mut receive_server_requests) =
3263 new_realm_builder_and_server_task();
3264 let _child_a = builder
3265 .add_child_from_decl("a", cm_rust::ComponentDecl::default(), ChildOptions::new())
3266 .await
3267 .unwrap();
3268 assert_matches!(
3269 receive_server_requests.next().await,
3270 Some(ServerRequest::AddChildFromDecl { name, decl, options })
3271 if &name == "a"
3272 && decl == fdecl::Component::default()
3273 && options == ChildOptions::new().into()
3274 );
3275 assert_matches!(receive_server_requests.next().now_or_never(), None);
3276 }
3277
3278 #[fuchsia::test]
3279 async fn add_local_child() {
3280 let (builder, _server_task, mut receive_server_requests) =
3281 new_realm_builder_and_server_task();
3282 let _child_a = builder
3283 .add_local_child("a", |_| async move { Ok(()) }.boxed(), ChildOptions::new())
3284 .await
3285 .unwrap();
3286 assert_matches!(
3287 receive_server_requests.next().await,
3288 Some(ServerRequest::AddLocalChild { name, options })
3289 if &name == "a" && options == ChildOptions::new().into()
3290 );
3291 assert_matches!(receive_server_requests.next().now_or_never(), None);
3292 }
3293
3294 #[fuchsia::test]
3295 async fn add_child_realm() {
3296 let (builder, _server_task, mut receive_server_requests) =
3297 new_realm_builder_and_server_task();
3298 let child_realm_a = builder.add_child_realm("a", ChildOptions::new()).await.unwrap();
3299 let _child_b = child_realm_a.add_child("b", "test://b", ChildOptions::new()).await.unwrap();
3300 let child_realm_c = builder
3301 .add_child_realm_from_relative_url("c", "#c", ChildOptions::new())
3302 .await
3303 .unwrap();
3304 let _child_d = child_realm_c.add_child("d", "test://d", ChildOptions::new()).await.unwrap();
3305 let child_realm_e = builder
3306 .add_child_realm_from_decl("e", cm_rust::ComponentDecl::default(), ChildOptions::new())
3307 .await
3308 .unwrap();
3309 let _child_f = child_realm_e.add_child("f", "test://f", ChildOptions::new()).await.unwrap();
3310
3311 let mut receive_sub_realm_requests =
3312 assert_add_child_realm(&mut receive_server_requests, "a", ChildOptions::new().into());
3313 assert_matches!(
3314 receive_sub_realm_requests.next().await,
3315 Some(ServerRequest::AddChild { name, url, options })
3316 if &name == "b" && &url == "test://b" && options == ChildOptions::new().into()
3317 );
3318 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3319
3320 let mut receive_sub_realm_requests = assert_add_child_realm_from_relative_url(
3321 &mut receive_server_requests,
3322 "c",
3323 "#c",
3324 ChildOptions::new().into(),
3325 );
3326 assert_matches!(
3327 receive_sub_realm_requests.next().await,
3328 Some(ServerRequest::AddChild { name, url, options })
3329 if &name == "d" && &url == "test://d" && options == ChildOptions::new().into()
3330 );
3331 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3332
3333 let mut receive_sub_realm_requests = assert_add_child_realm_from_decl(
3334 &mut receive_server_requests,
3335 "e",
3336 &fdecl::Component::default(),
3337 ChildOptions::new().into(),
3338 );
3339 assert_matches!(
3340 receive_sub_realm_requests.next().await,
3341 Some(ServerRequest::AddChild { name, url, options })
3342 if &name == "f" && &url == "test://f" && options == ChildOptions::new().into()
3343 );
3344 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3345 assert_matches!(receive_server_requests.next().now_or_never(), None);
3346 }
3347
3348 #[fuchsia::test]
3349 async fn get_component_decl() {
3350 let (builder, _server_task, mut receive_server_requests) =
3351 new_realm_builder_and_server_task();
3352 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3353 let _decl = builder.get_component_decl(&child_a).await.unwrap();
3354
3355 assert_matches!(
3356 receive_server_requests.next().await,
3357 Some(ServerRequest::AddChild { name, url, options })
3358 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3359 );
3360 assert_matches!(
3361 receive_server_requests.next().await,
3362 Some(ServerRequest::GetComponentDecl { name }) if &name == "a"
3363 );
3364 assert_matches!(receive_server_requests.next().now_or_never(), None);
3365 }
3366
3367 #[fuchsia::test]
3368 async fn replace_component_decl() {
3369 let (builder, _server_task, mut receive_server_requests) =
3370 new_realm_builder_and_server_task();
3371 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3372 builder.replace_component_decl(&child_a, cm_rust::ComponentDecl::default()).await.unwrap();
3373
3374 assert_matches!(
3375 receive_server_requests.next().await,
3376 Some(ServerRequest::AddChild { name, url, options })
3377 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3378 );
3379 assert_matches!(
3380 receive_server_requests.next().await,
3381 Some(ServerRequest::ReplaceComponentDecl { name, component_decl })
3382 if &name == "a" && component_decl == fdecl::Component::default()
3383 );
3384 assert_matches!(receive_server_requests.next().now_or_never(), None);
3385 }
3386
3387 #[fuchsia::test]
3388 async fn get_realm_decl() {
3389 let (builder, _server_task, mut receive_server_requests) =
3390 new_realm_builder_and_server_task();
3391 let _decl = builder.get_realm_decl().await.unwrap();
3392
3393 assert_matches!(receive_server_requests.next().await, Some(ServerRequest::GetRealmDecl));
3394 assert_matches!(receive_server_requests.next().now_or_never(), None);
3395 }
3396
3397 #[fuchsia::test]
3398 async fn replace_realm_decl() {
3399 let (builder, _server_task, mut receive_server_requests) =
3400 new_realm_builder_and_server_task();
3401 builder.replace_realm_decl(cm_rust::ComponentDecl::default()).await.unwrap();
3402
3403 assert_matches!(
3404 receive_server_requests.next().await,
3405 Some(ServerRequest::ReplaceRealmDecl { component_decl })
3406 if component_decl == fdecl::Component::default()
3407 );
3408 assert_matches!(receive_server_requests.next().now_or_never(), None);
3409 }
3410
3411 #[fuchsia::test]
3412 async fn set_config_value() {
3413 let (builder, _server_task, mut receive_server_requests) =
3414 new_realm_builder_and_server_task();
3415 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3416 builder.init_mutable_config_from_package(&child_a).await.unwrap();
3417 builder.init_mutable_config_to_empty(&child_a).await.unwrap();
3418 builder.set_config_value(&child_a, "test_bool", false.into()).await.unwrap();
3419 builder.set_config_value(&child_a, "test_int16", (-2 as i16).into()).await.unwrap();
3420 builder.set_config_value(&child_a, "test_string", "test".to_string().into()).await.unwrap();
3421 builder
3422 .set_config_value(&child_a, "test_string_vector", vec!["hello", "fuchsia"].into())
3423 .await
3424 .unwrap();
3425
3426 assert_matches!(
3427 receive_server_requests.next().await,
3428 Some(ServerRequest::AddChild { name, url, options })
3429 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3430 );
3431
3432 assert_matches!(
3433 receive_server_requests.next().await,
3434 Some(ServerRequest::InitMutableConfigFromPackage { name }) if &name == "a"
3435 );
3436
3437 assert_matches!(
3438 receive_server_requests.next().await,
3439 Some(ServerRequest::InitMutableConfigToEmpty { name }) if &name == "a"
3440 );
3441
3442 assert_matches!(
3443 receive_server_requests.next().await,
3444 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3445 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(boolean))), ..
3446 }}) if &name == "a" && &key == "test_bool" && boolean == false
3447 );
3448
3449 assert_matches!(
3450 receive_server_requests.next().await,
3451 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3452 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Int16(int16))), ..
3453 }}) if &name == "a" && &key == "test_int16" && int16 == -2
3454 );
3455
3456 assert_matches!(
3457 receive_server_requests.next().await,
3458 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3459 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::String(string))), ..
3460 }}) if &name == "a" && &key == "test_string" && &string == "test"
3461 );
3462
3463 assert_matches!(
3464 receive_server_requests.next().await,
3465 Some(ServerRequest::SetConfigValue { name, key, value: fdecl::ConfigValueSpec {
3466 value: Some(fdecl::ConfigValue::Vector(fdecl::ConfigVectorValue::StringVector(string_vector))), ..
3467 }}) if &name == "a" && &key == "test_string_vector" && string_vector == vec!["hello", "fuchsia"]
3468 );
3469
3470 assert_matches!(receive_server_requests.next().now_or_never(), None);
3471 }
3472
3473 #[fuchsia::test]
3474 async fn add_route() {
3475 let (builder, _server_task, mut receive_server_requests) =
3476 new_realm_builder_and_server_task();
3477 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3478 builder
3479 .add_route(
3480 Route::new()
3481 .capability(Capability::protocol_by_name("test"))
3482 .capability(Capability::directory("test2"))
3483 .capability(Capability::service_by_name("test3"))
3484 .capability(Capability::configuration("test4"))
3485 .capability(Capability::dictionary("test5"))
3486 .from(&child_a)
3487 .to(Ref::parent()),
3488 )
3489 .await
3490 .unwrap();
3491
3492 assert_matches!(
3493 receive_server_requests.next().await,
3494 Some(ServerRequest::AddChild { name, url, options })
3495 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3496 );
3497 assert_matches!(
3498 receive_server_requests.next().await,
3499 Some(ServerRequest::AddRouteFromDictionary { capabilities, from, to, from_dictionary })
3500 if capabilities == vec![
3501 Capability::protocol_by_name("test").into(),
3502 Capability::directory("test2").into(),
3503 Capability::service_by_name("test3").into(),
3504 Capability::configuration("test4").into(),
3505 Capability::dictionary("test5").into(),
3506 ]
3507 && from == Ref::child("a").into_fidl(RefContext::Source).0
3508 && to == vec![Ref::parent().into_fidl(RefContext::Target).0]
3509 && from_dictionary == "."
3510 );
3511 assert_matches!(receive_server_requests.next().now_or_never(), None);
3512 }
3513
3514 #[fuchsia::test]
3515 async fn add_route_to_dictionary() {
3516 let (builder, _server_task, mut receive_server_requests) =
3517 new_realm_builder_and_server_task();
3518 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3519 builder
3520 .add_capability(cm_rust::CapabilityDecl::Dictionary(cm_rust::DictionaryDecl {
3521 name: "my_dict".parse().unwrap(),
3522 source_path: None,
3523 }))
3524 .await
3525 .unwrap();
3526 builder
3527 .add_route(
3528 Route::new()
3529 .capability(Capability::protocol_by_name("test"))
3530 .capability(Capability::directory("test2"))
3531 .capability(Capability::service_by_name("test3"))
3532 .capability(Capability::dictionary("test4"))
3533 .from(&child_a)
3534 .to(Ref::dictionary(Ref::self_(), "my_dict")),
3535 )
3536 .await
3537 .unwrap();
3538
3539 assert_matches!(
3540 receive_server_requests.next().await,
3541 Some(ServerRequest::AddChild { name, url, options })
3542 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3543 );
3544 assert_matches!(
3545 receive_server_requests.next().await,
3546 Some(ServerRequest::AddCapability { .. })
3547 );
3548 assert_matches!(
3549 receive_server_requests.next().await,
3550 Some(ServerRequest::AddRouteFromDictionary { capabilities, from, to, from_dictionary })
3551 if capabilities == vec![
3552 Capability::protocol_by_name("test").into(),
3553 Capability::directory("test2").into(),
3554 Capability::service_by_name("test3").into(),
3555 Capability::dictionary("test4").into(),
3556 ]
3557 && from == Ref::child("a").into_fidl(RefContext::Source).0
3558 && to == vec![Ref::dictionary(Ref::self_(), "my_dict").into_fidl(RefContext::Target).0]
3559 && from_dictionary == "."
3560 );
3561 assert_matches!(receive_server_requests.next().now_or_never(), None);
3562 }
3563
3564 #[fuchsia::test]
3565 async fn add_route_from_dictionary() {
3566 let (builder, _server_task, mut receive_server_requests) =
3567 new_realm_builder_and_server_task();
3568 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3569 builder
3570 .add_route(
3571 Route::new()
3572 .capability(Capability::protocol_by_name("test"))
3573 .capability(Capability::directory("test2"))
3574 .capability(Capability::service_by_name("test3"))
3575 .capability(Capability::dictionary("test4"))
3576 .from(Ref::dictionary(&child_a, "source/dict"))
3577 .to(Ref::parent()),
3578 )
3579 .await
3580 .unwrap();
3581
3582 assert_matches!(
3583 receive_server_requests.next().await,
3584 Some(ServerRequest::AddChild { name, url, options })
3585 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3586 );
3587 assert_matches!(
3588 receive_server_requests.next().await,
3589 Some(ServerRequest::AddRouteFromDictionary { capabilities, from, to, from_dictionary })
3590 if capabilities == vec![
3591 Capability::protocol_by_name("test").into(),
3592 Capability::directory("test2").into(),
3593 Capability::service_by_name("test3").into(),
3594 Capability::dictionary("test4").into(),
3595 ]
3596 && from == Ref::child("a").into_fidl(RefContext::Source).0
3597 && to == vec![Ref::parent().into_fidl(RefContext::Target).0]
3598 && from_dictionary == "source/dict"
3599 );
3600 assert_matches!(receive_server_requests.next().now_or_never(), None);
3601 }
3602
3603 #[fuchsia::test]
3604 async fn add_child_to_sub_realm() {
3605 let (builder, _server_task, mut receive_server_requests) =
3606 new_realm_builder_and_server_task();
3607 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3608 let _child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3609 let mut receive_sub_realm_requests =
3610 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3611 assert_matches!(
3612 receive_sub_realm_requests.next().await,
3613 Some(ServerRequest::AddChild { name, url, options })
3614 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3615 );
3616 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3617 assert_matches!(receive_server_requests.next().now_or_never(), None);
3618 }
3619
3620 #[fuchsia::test]
3621 async fn add_child_from_decl_to_sub_realm() {
3622 let (builder, _server_task, mut receive_server_requests) =
3623 new_realm_builder_and_server_task();
3624 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3625 let _child_a = child_realm
3626 .add_child_from_decl("a", cm_rust::ComponentDecl::default(), ChildOptions::new())
3627 .await
3628 .unwrap();
3629 let mut receive_sub_realm_requests =
3630 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3631 assert_matches!(
3632 receive_sub_realm_requests.next().await,
3633 Some(ServerRequest::AddChildFromDecl { name, decl, options })
3634 if &name == "a"
3635 && decl == fdecl::Component::default()
3636 && options == ChildOptions::new().into()
3637 );
3638 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3639 assert_matches!(receive_server_requests.next().now_or_never(), None);
3640 }
3641
3642 #[fuchsia::test]
3643 async fn add_local_child_to_sub_realm() {
3644 let (builder, _server_task, mut receive_server_requests) =
3645 new_realm_builder_and_server_task();
3646 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3647 let _child_a = child_realm
3648 .add_local_child("a", |_| async move { Ok(()) }.boxed(), ChildOptions::new())
3649 .await
3650 .unwrap();
3651 let mut receive_sub_realm_requests =
3652 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3653 assert_matches!(
3654 receive_sub_realm_requests.next().await,
3655 Some(ServerRequest::AddLocalChild { name, options })
3656 if &name == "a" && options == ChildOptions::new().into()
3657 );
3658 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3659 assert_matches!(receive_server_requests.next().now_or_never(), None);
3660 }
3661
3662 #[fuchsia::test]
3663 async fn add_child_realm_to_child_realm() {
3664 let (builder, _server_task, mut receive_server_requests) =
3665 new_realm_builder_and_server_task();
3666 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3667 let child_realm_a = child_realm.add_child_realm("a", ChildOptions::new()).await.unwrap();
3668 let _child_b = child_realm_a.add_child("b", "test://b", ChildOptions::new()).await.unwrap();
3669
3670 let mut receive_sub_realm_requests =
3671 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3672 let mut receive_sub_sub_realm_requests = assert_add_child_realm(
3673 &mut receive_sub_realm_requests,
3674 "a",
3675 ChildOptions::new().into(),
3676 );
3677 assert_matches!(
3678 receive_sub_sub_realm_requests.next().await,
3679 Some(ServerRequest::AddChild { name, url, options })
3680 if &name == "b" && &url == "test://b" && options == ChildOptions::new().into()
3681 );
3682 assert_matches!(receive_sub_sub_realm_requests.next().now_or_never(), None);
3683 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3684 assert_matches!(receive_server_requests.next().now_or_never(), None);
3685 }
3686
3687 #[fuchsia::test]
3688 async fn get_component_decl_in_sub_realm() {
3689 let (builder, _server_task, mut receive_server_requests) =
3690 new_realm_builder_and_server_task();
3691 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3692 let child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3693 let _decl = child_realm.get_component_decl(&child_a).await.unwrap();
3694
3695 let mut receive_sub_realm_requests =
3696 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3697 assert_matches!(
3698 receive_sub_realm_requests.next().await,
3699 Some(ServerRequest::AddChild { name, url, options })
3700 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3701 );
3702 assert_matches!(
3703 receive_sub_realm_requests.next().await,
3704 Some(ServerRequest::GetComponentDecl { name }) if &name == "a"
3705 );
3706 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3707 assert_matches!(receive_server_requests.next().now_or_never(), None);
3708 }
3709
3710 #[fuchsia::test]
3711 async fn replace_component_decl_in_sub_realm() {
3712 let (builder, _server_task, mut receive_server_requests) =
3713 new_realm_builder_and_server_task();
3714 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3715 let child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3716 child_realm
3717 .replace_component_decl(&child_a, cm_rust::ComponentDecl::default())
3718 .await
3719 .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::ReplaceComponentDecl { name, component_decl })
3731 if &name == "a" && component_decl == fdecl::Component::default()
3732 );
3733 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3734 assert_matches!(receive_server_requests.next().now_or_never(), None);
3735 }
3736
3737 #[fuchsia::test]
3738 async fn get_realm_decl_in_sub_realm() {
3739 let (builder, _server_task, mut receive_server_requests) =
3740 new_realm_builder_and_server_task();
3741 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3742 let _decl = child_realm.get_realm_decl().await.unwrap();
3743
3744 let mut receive_sub_realm_requests =
3745 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3746 assert_matches!(receive_sub_realm_requests.next().await, Some(ServerRequest::GetRealmDecl));
3747 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3748 assert_matches!(receive_server_requests.next().now_or_never(), None);
3749 }
3750
3751 #[fuchsia::test]
3752 async fn replace_realm_decl_in_sub_realm() {
3753 let (builder, _server_task, mut receive_server_requests) =
3754 new_realm_builder_and_server_task();
3755 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3756 child_realm.replace_realm_decl(cm_rust::ComponentDecl::default()).await.unwrap();
3757
3758 let mut receive_sub_realm_requests =
3759 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3760 assert_matches!(
3761 receive_sub_realm_requests.next().await,
3762 Some(ServerRequest::ReplaceRealmDecl { component_decl })
3763 if component_decl == fdecl::Component::default()
3764 );
3765 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3766 assert_matches!(receive_server_requests.next().now_or_never(), None);
3767 }
3768
3769 #[fuchsia::test]
3770 async fn add_route_in_sub_realm() {
3771 let (builder, _server_task, mut receive_server_requests) =
3772 new_realm_builder_and_server_task();
3773 let child_realm = builder.add_child_realm("1", ChildOptions::new()).await.unwrap();
3774 let child_a = child_realm.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3775 child_realm
3776 .add_route(
3777 Route::new()
3778 .capability(Capability::protocol_by_name("test"))
3779 .capability(Capability::directory("test2"))
3780 .from(&child_a)
3781 .to(Ref::parent()),
3782 )
3783 .await
3784 .unwrap();
3785
3786 let mut receive_sub_realm_requests =
3787 assert_add_child_realm(&mut receive_server_requests, "1", ChildOptions::new().into());
3788 assert_matches!(
3789 receive_sub_realm_requests.next().await,
3790 Some(ServerRequest::AddChild { name, url, options })
3791 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3792 );
3793 assert_matches!(
3794 receive_sub_realm_requests.next().await,
3795 Some(ServerRequest::AddRouteFromDictionary { capabilities, from, to, from_dictionary })
3796 if capabilities == vec![
3797 Capability::protocol_by_name("test").into(),
3798 Capability::directory("test2").into(),
3799 ]
3800 && from == Ref::child("a").into_fidl(RefContext::Source).0
3801 && to == vec![Ref::parent().into_fidl(RefContext::Target).0]
3802 && from_dictionary == "."
3803 );
3804 assert_matches!(receive_sub_realm_requests.next().now_or_never(), None);
3805 assert_matches!(receive_server_requests.next().now_or_never(), None);
3806 }
3807
3808 #[fuchsia::test]
3809 async fn read_only_directory() {
3810 let (builder, _server_task, mut receive_server_requests) =
3811 new_realm_builder_and_server_task();
3812 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3813 builder
3814 .read_only_directory(
3815 "config",
3816 vec![&child_a],
3817 DirectoryContents::new().add_file("config.json", "{ \"hippos\": \"rule!\" }"),
3818 )
3819 .await
3820 .unwrap();
3821
3822 assert_matches!(
3823 receive_server_requests.next().await,
3824 Some(ServerRequest::AddChild { name, url, options })
3825 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3826 );
3827 assert_read_only_directory(&mut receive_server_requests, "config", vec![&child_a]);
3828 }
3829
3830 #[fuchsia::test]
3831 async fn storage() {
3832 let (builder, _server_task, mut receive_server_requests) =
3833 new_realm_builder_and_server_task();
3834 let child_a = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3835 builder.add_storage("data", vec![&child_a], None).await.unwrap();
3836
3837 assert_matches!(
3838 receive_server_requests.next().await,
3839 Some(ServerRequest::AddChild { name, url, options })
3840 if &name == "a" && &url == "test://a" && options == ChildOptions::new().into()
3841 );
3842 assert_matches!(
3843 receive_server_requests.next().await,
3844 Some(ServerRequest::AddStorage { name, to })
3845 if &name == "data" && &to == &vec![fdecl::Ref::Child(fdecl::ChildRef { name: "a".to_string(), collection: None })]
3846 );
3847 }
3848
3849 #[test]
3850 fn realm_builder_works_with_send() {
3851 let mut executor = fasync::SendExecutorBuilder::new().num_threads(2).build();
3854 executor.run(async {
3855 let (builder, _server_task, _receive_server_requests) =
3856 new_realm_builder_and_server_task();
3857 let child_realm_a = builder.add_child_realm("a", ChildOptions::new()).await.unwrap();
3858 let child_b = builder
3859 .add_local_child("b", |_handles| pending().boxed(), ChildOptions::new())
3860 .await
3861 .unwrap();
3862 let child_c = builder.add_child("c", "test://c", ChildOptions::new()).await.unwrap();
3863 let child_e = builder
3864 .add_child_from_decl("e", cm_rust::ComponentDecl::default(), ChildOptions::new())
3865 .await
3866 .unwrap();
3867
3868 let decl_for_e = builder.get_component_decl(&child_e).await.unwrap();
3869 builder.replace_component_decl(&child_e, decl_for_e).await.unwrap();
3870 let realm_decl = builder.get_realm_decl().await.unwrap();
3871 builder.replace_realm_decl(realm_decl).await.unwrap();
3872 builder
3873 .add_route(
3874 Route::new()
3875 .capability(Capability::protocol::<fcomponent::RealmMarker>())
3876 .from(&child_e)
3877 .to(&child_c)
3878 .to(&child_b)
3879 .to(&child_realm_a)
3880 .to(Ref::parent()),
3881 )
3882 .await
3883 .unwrap();
3884 builder
3885 .read_only_directory(
3886 "config",
3887 vec![&child_e],
3888 DirectoryContents::new().add_file("config.json", "{ \"hippos\": \"rule!\" }"),
3889 )
3890 .await
3891 .unwrap();
3892 });
3893 }
3894
3895 #[fuchsia::test]
3896 async fn add_configurations() {
3897 let (builder, _server_task, mut receive_server_requests) =
3898 new_realm_builder_and_server_task();
3899 _ = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3900 _ = receive_server_requests.next().now_or_never();
3901
3902 builder
3903 .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
3904 name: "my-config".to_string().fidl_into_native(),
3905 value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(true)),
3906 }))
3907 .await
3908 .unwrap();
3909 match receive_server_requests.next().now_or_never() {
3910 Some(Some(ServerRequest::AddCapability { capability, .. })) => {
3911 let configuration = assert_matches!(capability, fdecl::Capability::Config(c) => c);
3912 assert_eq!(configuration.name, Some("my-config".to_string()));
3913 assert_eq!(
3914 configuration.value,
3915 Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true)))
3916 );
3917 }
3918 req => panic!("match failed, received unexpected server request: {:?}", req),
3919 };
3920 }
3921
3922 #[fuchsia::test]
3923 async fn add_environment_and_collection() {
3924 let (builder, _server_task, mut receive_server_requests) =
3925 new_realm_builder_and_server_task();
3926 _ = builder.add_child("a", "test://a", ChildOptions::new()).await.unwrap();
3927 _ = receive_server_requests.next().now_or_never();
3928
3929 builder
3930 .add_environment(cm_rust::EnvironmentDecl {
3931 name: "driver-host-env".parse().unwrap(),
3932 extends: fdecl::EnvironmentExtends::Realm,
3933 runners: Box::from([]),
3934 resolvers: Box::from([cm_rust::ResolverRegistration {
3935 resolver: "boot-resolver".parse().unwrap(),
3936 source: cm_rust::RegistrationSource::Child("fake-resolver".to_string()),
3937 scheme: "fuchsia-boot".to_string(),
3938 }]),
3939 debug_capabilities: Box::from([]),
3940 stop_timeout_ms: Some(20000),
3941 })
3942 .await
3943 .unwrap();
3944 match receive_server_requests.next().now_or_never() {
3945 Some(Some(ServerRequest::AddEnvironment { environment, .. })) => {
3946 assert_eq!(environment.name, Some("driver-host-env".to_string()));
3947 }
3948 req => panic!("match failed, received unexpected server request: {:?}", req),
3949 };
3950 builder
3951 .add_collection(cm_rust::CollectionDecl {
3952 name: "driver-hosts".parse().unwrap(),
3953 durability: fdecl::Durability::SingleRun,
3954 environment: Some("driver-host-env".parse().unwrap()),
3955 allowed_offers: Default::default(),
3956 allow_long_names: Default::default(),
3957 persistent_storage: None,
3958 })
3959 .await
3960 .unwrap();
3961 match receive_server_requests.next().now_or_never() {
3962 Some(Some(ServerRequest::AddCollection { collection, .. })) => {
3963 assert_eq!(collection.name, Some("driver-hosts".to_string()));
3964 }
3965 req => panic!("match failed, received unexpected server request: {:?}", req),
3966 };
3967 }
3968}