fuchsia_component_test/
lib.rs

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