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