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