fuchsia_component_test/
lib.rs

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