fuchsia_driver_test/
builder.rs

1// Copyright 2025 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 anyhow::{Context as _, Result};
6use fidl::HandleBased;
7
8use cm_rust::FidlIntoNative;
9use fuchsia_component::server::ServiceFs;
10use fuchsia_component_test::{
11    Capability, ChildOptions, ChildRef, CollectionRef, LocalComponentHandles, RealmBuilder,
12    RealmInstance, Ref, Route,
13};
14use futures::{StreamExt, TryStreamExt};
15use std::sync::Arc;
16use {
17    fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_component_test as ftest,
18    fidl_fuchsia_driver_development as fdd, fidl_fuchsia_driver_test as fdt,
19    fidl_fuchsia_io as fio, fuchsia_async as fasync,
20};
21
22fn clone(
23    dir: &fio::DirectoryProxy,
24) -> Result<fidl::endpoints::ClientEnd<fio::DirectoryMarker>, fidl::Error> {
25    let (client_end, server_end) = fidl::endpoints::create_endpoints::<fio::DirectoryMarker>();
26    dir.clone(fidl::endpoints::ServerEnd::new(server_end.into_channel()))?;
27    Ok(client_end)
28}
29
30async fn internal_serve(
31    stream: fidl_fuchsia_driver_test::InternalRequestStream,
32    test_pkg_dir: Arc<fio::DirectoryProxy>,
33    test_resolution_context: Arc<Option<fidl_fuchsia_component_resolution::Context>>,
34    boot_dir: Arc<Option<fio::DirectoryProxy>>,
35    boot_driver_components: Arc<Option<Vec<String>>>,
36) {
37    stream
38        .try_for_each_concurrent(None, |request| {
39            let test_pkg_dir = test_pkg_dir.clone();
40            let test_resolution_context = test_resolution_context.clone();
41            let boot_dir = boot_dir.clone();
42            let boot_driver_components = boot_driver_components.clone();
43            async move {
44                match request {
45                    fidl_fuchsia_driver_test::InternalRequest::GetTestPackage { responder } => {
46                        let cloned = clone(test_pkg_dir.as_ref())?;
47                        responder.send(Ok(Some(cloned)))?;
48                    }
49                    fidl_fuchsia_driver_test::InternalRequest::GetTestResolutionContext {
50                        responder,
51                    } => responder.send(Ok(test_resolution_context.as_ref().as_ref()))?,
52                    fidl_fuchsia_driver_test::InternalRequest::GetBootDirectory { responder } => {
53                        match boot_dir.as_ref() {
54                            Some(boot_dir) => {
55                                let cloned = clone(boot_dir)?;
56                                responder.send(Ok(Some(cloned)))?;
57                            }
58                            None => responder.send(Ok(None))?,
59                        }
60                    }
61                    fidl_fuchsia_driver_test::InternalRequest::GetBootDriverOverrides {
62                        responder,
63                    } => responder.send(Ok(boot_driver_components
64                        .as_ref()
65                        .clone()
66                        .unwrap_or(vec![])
67                        .as_slice()))?,
68                };
69                Ok(())
70            }
71        })
72        .await
73        .expect("fuchsia.driver.test.Internal failed.");
74}
75
76async fn resource_provider_serve(
77    stream: fidl_fuchsia_driver_test::ResourceProviderRequestStream,
78    devicetree: Arc<Option<zx::Vmo>>,
79) {
80    stream
81        .try_for_each_concurrent(None, |request| {
82            let devicetree = devicetree.clone();
83            async move {
84                match request {
85                    fidl_fuchsia_driver_test::ResourceProviderRequest::GetDeviceTree {
86                        responder,
87                    } => {
88                        if let Some(vmo) = devicetree.as_ref().as_ref().map(|d| {
89                            d.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("duplicate")
90                        }) {
91                            responder.send(Ok(vmo))?;
92                        } else {
93                            responder.send(Err(zx::Status::NOT_FOUND.into_raw()))?;
94                        }
95                    }
96                };
97                Ok(())
98            }
99        })
100        .await
101        .expect("fuchsia.driver.test.Internal failed.");
102}
103
104async fn run_internal_server(
105    handles: LocalComponentHandles,
106    test_pkg_dir: Arc<fio::DirectoryProxy>,
107    test_resolution_context: Arc<Option<fidl_fuchsia_component_resolution::Context>>,
108    boot_dir: Arc<Option<fio::DirectoryProxy>>,
109    boot_driver_components: Arc<Option<Vec<String>>>,
110    devicetree: Arc<Option<zx::Vmo>>,
111) -> Result<()> {
112    let mut fs = ServiceFs::new();
113
114    fs.dir("svc").add_fidl_service(
115        move |stream: fidl_fuchsia_driver_test::InternalRequestStream| {
116            fasync::Task::spawn(internal_serve(
117                stream,
118                test_pkg_dir.clone(),
119                test_resolution_context.clone(),
120                boot_dir.clone(),
121                boot_driver_components.clone(),
122            ))
123            .detach();
124        },
125    );
126    fs.dir("svc").add_fidl_service(
127        move |stream: fidl_fuchsia_driver_test::ResourceProviderRequestStream| {
128            fasync::Task::spawn(resource_provider_serve(stream, devicetree.clone())).detach();
129        },
130    );
131    fs.serve_connection(handles.outgoing_dir)?;
132    fs.collect::<()>().await;
133    Ok(())
134}
135
136// Basic equality check of just the name.
137fn capabilities_eq_name(a: &ftest::Capability, b: &ftest::Capability) -> bool {
138    match (a, b) {
139        (ftest::Capability::Protocol(a), ftest::Capability::Protocol(b)) => a.name == b.name,
140        (ftest::Capability::Directory(a), ftest::Capability::Directory(b)) => a.name == b.name,
141        (ftest::Capability::Storage(a), ftest::Capability::Storage(b)) => a.name == b.name,
142        (ftest::Capability::Service(a), ftest::Capability::Service(b)) => a.name == b.name,
143        (ftest::Capability::EventStream(a), ftest::Capability::EventStream(b)) => a.name == b.name,
144        (ftest::Capability::Config(a), ftest::Capability::Config(b)) => a.name == b.name,
145        (ftest::Capability::Dictionary(a), ftest::Capability::Dictionary(b)) => a.name == b.name,
146        (ftest::Capability::Resolver(a), ftest::Capability::Resolver(b)) => a.name == b.name,
147        (ftest::Capability::Runner(a), ftest::Capability::Runner(b)) => a.name == b.name,
148        _ => false,
149    }
150}
151
152#[derive(Debug, Clone, Default)]
153pub struct Options {
154    using_subpackage: Option<bool>,
155    driver_offers: Option<(Ref, Vec<ftest::Capability>)>,
156    driver_exposes: Option<Vec<ftest::Capability>>,
157    extra_realm_capabilities: Vec<(ftest::Capability, Ref)>,
158}
159
160impl Options {
161    pub fn new() -> Self {
162        Self::default()
163    }
164
165    pub fn using_subpackage(mut self, using_subpackage: bool) -> Self {
166        self.using_subpackage = Some(using_subpackage);
167        self
168    }
169
170    pub fn driver_offers(mut self, provider: Ref, offers: Vec<ftest::Capability>) -> Self {
171        self.driver_offers = Some((provider, offers));
172        self
173    }
174
175    pub fn driver_exposes(mut self, exposes: Vec<ftest::Capability>) -> Self {
176        self.driver_exposes = Some(exposes);
177        self
178    }
179
180    pub fn add_extra_realm_capability(
181        mut self,
182        capability: ftest::Capability,
183        from: impl Into<Ref>,
184    ) -> Self {
185        self.extra_realm_capabilities.push((capability, from.into()));
186        self
187    }
188}
189
190#[async_trait::async_trait]
191pub trait DriverTestRealmBuilder {
192    /// Set up the driver test realm.
193    /// This version is a collapsed version compared to the previous version which used a two layer
194    /// realm builder setup.
195    async fn driver_test_realm_setup(
196        &self,
197        options: Options,
198        args: fdt::RealmArgs,
199    ) -> Result<&Self>;
200}
201
202#[async_trait::async_trait]
203impl DriverTestRealmBuilder for RealmBuilder {
204    async fn driver_test_realm_setup(
205        &self,
206        options: Options,
207        args: fdt::RealmArgs,
208    ) -> Result<&Self> {
209        let manifest_provider =
210            fuchsia_component::client::connect_to_protocol::<fdt::ManifestProviderMarker>()?;
211        let stream = manifest_provider
212            .get_manifest(&fdt::GetManifestRequest {
213                using_subpackage: options.using_subpackage,
214                ..Default::default()
215            })
216            .await?
217            .expect("manifest stream");
218
219        let mut manifest: Vec<u8> = vec![];
220        loop {
221            let mut read = stream.read_to_vec(
222                zx::StreamReadOptions::empty(),
223                fidl_fuchsia_io::MAX_TRANSFER_SIZE as usize,
224            )?;
225            if read.is_empty() {
226                break;
227            }
228            manifest.append(&mut read);
229        }
230
231        let component = fidl::unpersist::<fdecl::Component>(manifest.as_slice())
232            .context("unpersisting the manifest vector")?;
233
234        // Keep the rust and c++ realm_builder setups in sync.
235        // LINT.IfChange
236        let realm = self
237            .add_child_realm_from_decl(
238                "driver_test_realm",
239                component.fidl_into_native(),
240                ChildOptions::new(),
241            )
242            .await?;
243
244        // From the test root into the dtr.
245        self.add_route(
246            Route::new()
247                .capability(Capability::protocol_by_name("fuchsia.diagnostics.ArchiveAccessor"))
248                .from(Ref::parent())
249                .to(&realm),
250        )
251        .await?;
252
253        let dtr_support: ChildRef = "dtr_support".into();
254        let fake_resolver: ChildRef = "fake_resolver".into();
255        let driver_manager: ChildRef = "driver_manager".into();
256        let driver_index: ChildRef = "driver_index".into();
257
258        let boot_drivers: CollectionRef = "boot-drivers".into();
259        let base_drivers: CollectionRef = "base-drivers".into();
260        let full_drivers: CollectionRef = "full-drivers".into();
261
262        let devicetree = Arc::new(args.devicetree);
263
264        // Get the test component information.
265        let test_component = if let Some(test_component) = args.test_component {
266            test_component
267        } else {
268            let realm = fuchsia_component::client::connect_to_protocol::<
269                fidl_fuchsia_component::RealmMarker,
270            >()?;
271
272            realm.get_resolved_info().await.unwrap().unwrap()
273        };
274
275        let test_resolution_context = Arc::new(test_component.resolution_context);
276        let test_pkg_dir = Arc::new(
277            test_component.package.expect("a pkg").directory.expect("a directory").into_proxy(),
278        );
279
280        let boot_dir = Arc::new(match args.boot {
281            Some(boot_dir) => {
282                if !boot_dir.is_invalid_handle() {
283                    Some(boot_dir.into_proxy())
284                } else {
285                    None
286                }
287            }
288            None => None,
289        });
290        let boot_driver_components = Arc::new(args.boot_driver_components);
291
292        // Route the resolvers from the test root.
293        self.add_route(
294            Route::new()
295                .capability(
296                    Capability::protocol_by_name("fuchsia.component.resolution.Resolver-hermetic")
297                        .optional(),
298                )
299                .capability(
300                    Capability::protocol_by_name("fuchsia.pkg.PackageResolver-hermetic").optional(),
301                )
302                .from(Ref::parent())
303                .to(&realm),
304        )
305        .await?;
306
307        // Setup the local Internal protocol server child.
308        let driver_test_internal = realm
309            .add_local_child(
310                "driver_test_internal",
311                move |handles| {
312                    let test_pkg_dir = test_pkg_dir.clone();
313                    let test_resolution_context = test_resolution_context.clone();
314                    let boot_dir = boot_dir.clone();
315                    let boot_driver_components = boot_driver_components.clone();
316                    let devicetree = devicetree.clone();
317
318                    Box::pin(run_internal_server(
319                        handles,
320                        test_pkg_dir,
321                        test_resolution_context,
322                        boot_dir,
323                        boot_driver_components,
324                        devicetree,
325                    ))
326                },
327                ChildOptions::new(),
328            )
329            .await?;
330
331        // These are capabilities that are routed from void by default but can be provided manually
332        // from the user through extra_realm_capabilities.
333        let mut tunnel_boot_items = false;
334        let mut voided_offers: Vec<ftest::Capability> = vec![
335            Capability::protocol_by_name("fuchsia.tracing.provider.Registry").optional().into(),
336            Capability::protocol_by_name("fuchsia.boot.WriteOnlyLog").optional().into(),
337            Capability::protocol_by_name("fuchsia.scheduler.RoleManager").optional().into(),
338            Capability::protocol_by_name("fuchsia.boot.Items").optional().into(),
339            Capability::protocol_by_name("fuchsia.kernel.IommuResource").optional().into(),
340            Capability::protocol_by_name("fuchsia.diagnostics.LogFlusher").optional().into(),
341            Capability::protocol_by_name("fuchsia.kernel.MexecResource").optional().into(),
342            Capability::protocol_by_name("fuchsia.kernel.PowerResource").optional().into(),
343        ];
344        for (capability, from) in options.extra_realm_capabilities {
345            // Remove the default voiding for any user provided capabilities.
346            voided_offers.retain(|voided| !capabilities_eq_name(voided, &capability));
347
348            if from != Ref::void()
349                && capabilities_eq_name(
350                    &Capability::protocol_by_name("fuchsia.boot.Items").into(),
351                    &capability,
352                )
353            {
354                tunnel_boot_items = true;
355            }
356
357            self.add_route(Route::new().capability(capability).from(from).to(&realm)).await?;
358        }
359
360        // Set the default void route for remaining voided offers.
361        for voided in voided_offers {
362            self.add_route(Route::new().capability(voided).from(Ref::void()).to(&realm)).await?;
363        }
364
365        // Provide offers from the driver_offers, if the test provides one, to the driver
366        // collections.
367        if args.dtr_offers.is_some() {
368            panic!("Please use |Options::driver_offers| instead of dtr_offers.")
369        }
370        if let Some((provider, offers)) = options.driver_offers {
371            for offer in offers {
372                self.add_route(
373                    Route::new().capability(offer.clone()).from(provider.clone()).to(&realm),
374                )
375                .await?;
376                realm
377                    .add_route(
378                        Route::new()
379                            .capability(offer)
380                            .from(Ref::parent())
381                            .to(&boot_drivers)
382                            .to(&base_drivers)
383                            .to(&full_drivers),
384                    )
385                    .await?;
386            }
387        }
388
389        // Provide exposes from the driver collections to the test.
390        if args.dtr_exposes.is_some() {
391            panic!("Please use |Options::driver_exposes| instead of dtr_exposes.")
392        }
393        if let Some(exposes) = options.driver_exposes {
394            for expose in exposes {
395                realm
396                    .add_route(
397                        Route::new()
398                            .capability(expose.clone())
399                            .from(&boot_drivers)
400                            .to(Ref::parent()),
401                    )
402                    .await?;
403                realm
404                    .add_route(
405                        Route::new()
406                            .capability(expose.clone())
407                            .from(&base_drivers)
408                            .to(Ref::parent()),
409                    )
410                    .await?;
411                realm
412                    .add_route(
413                        Route::new()
414                            .capability(expose.clone())
415                            .from(&full_drivers)
416                            .to(Ref::parent()),
417                    )
418                    .await?;
419
420                self.add_route(Route::new().capability(expose).from(&realm).to(Ref::parent()))
421                    .await?;
422            }
423        }
424
425        // Setup the driver test resource provider.
426        realm
427            .add_route(
428                Route::new()
429                    .capability(Capability::protocol_by_name(
430                        "fuchsia.driver.test.ResourceProvider",
431                    ))
432                    .from(&driver_test_internal)
433                    .to(&dtr_support),
434            )
435            .await?;
436
437        // Setup various basic config capabilities.
438        realm
439            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
440                name: "fuchsia.driver.testrealm.TunnelBootItems".parse()?,
441                value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(
442                    tunnel_boot_items,
443                )),
444            }))
445            .await?;
446
447        realm
448            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
449                name: "fuchsia.driver.testrealm.BoardName".parse()?,
450                value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::String(
451                    args.board_name.unwrap_or_default(),
452                )),
453            }))
454            .await?;
455
456        realm
457            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
458                name: "fuchsia.driver.testrealm.PlatformVid".parse()?,
459                value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::String(
460                    args.platform_vid.map(|v| v.to_string()).unwrap_or_default(),
461                )),
462            }))
463            .await?;
464
465        realm
466            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
467                name: "fuchsia.driver.testrealm.PlatformPid".parse()?,
468                value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::String(
469                    args.platform_pid.map(|v| v.to_string()).unwrap_or_default(),
470                )),
471            }))
472            .await?;
473
474        let bind_eager = args.driver_bind_eager.unwrap_or_default();
475        realm
476            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
477                name: "fuchsia.driver.BindEager".parse()?,
478                value: cm_rust::ConfigValue::Vector(cm_rust::ConfigVectorValue::StringVector(
479                    bind_eager.into(),
480                )),
481            }))
482            .await?;
483
484        let driver_disable = args.driver_disable.unwrap_or_default();
485        realm
486            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
487                name: "fuchsia.driver.DisabledDrivers".parse()?,
488                value: cm_rust::ConfigValue::Vector(cm_rust::ConfigVectorValue::StringVector(
489                    driver_disable.into(),
490                )),
491            }))
492            .await?;
493
494        let driver_index_stop_timeout_millis = args.driver_index_stop_timeout_millis.unwrap_or(-1);
495        realm
496            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
497                name: "fuchsia.driver.index.StopOnIdleTimeoutMillis".parse()?,
498                value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Int64(
499                    driver_index_stop_timeout_millis,
500                )),
501            }))
502            .await?;
503        realm
504            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
505                name: "fuchsia.power.WaitForSuspendingToken".parse()?,
506                value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::Bool(false)),
507            }))
508            .await?;
509
510        let root_driver = match args.root_driver {
511            Some(val) => val,
512            None => "fuchsia-boot:///dtr#meta/test-parent-sys.cm".to_string(),
513        };
514        realm
515            .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
516                name: "fuchsia.driver.manager.RootDriver".parse()?,
517                value: cm_rust::ConfigValue::Single(cm_rust::ConfigSingleValue::String(
518                    root_driver,
519                )),
520            }))
521            .await?;
522
523        // Setup software device config capabilities.
524        let software_devs_src = match args.software_devices {
525            Some(devs) => {
526                let names = devs.iter().map(|dev| dev.device_name.clone()).collect::<Vec<_>>();
527                let ids = devs.iter().map(|dev| dev.device_id).collect::<Vec<_>>();
528                realm
529                    .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
530                        name: "fuchsia.platform.bus.SoftwareDeviceNames".parse()?,
531                        value: cm_rust::ConfigValue::Vector(
532                            cm_rust::ConfigVectorValue::StringVector(names.into()),
533                        ),
534                    }))
535                    .await?;
536                realm
537                    .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
538                        name: "fuchsia.platform.bus.SoftwareDeviceIds".parse()?,
539                        value: cm_rust::ConfigValue::Vector(
540                            cm_rust::ConfigVectorValue::Uint32Vector(ids.into()),
541                        ),
542                    }))
543                    .await?;
544                Ref::self_()
545            }
546            None => Ref::void(),
547        };
548
549        // Config routes.
550        realm
551            .add_route(
552                Route::new()
553                    .capability(Capability::configuration("fuchsia.driver.BindEager"))
554                    .capability(Capability::configuration("fuchsia.driver.DisabledDrivers"))
555                    .capability(Capability::configuration(
556                        "fuchsia.driver.index.StopOnIdleTimeoutMillis",
557                    ))
558                    .from(Ref::self_())
559                    .to(&driver_index),
560            )
561            .await?;
562
563        realm
564            .add_route(
565                Route::new()
566                    .capability(Capability::configuration("fuchsia.driver.manager.RootDriver"))
567                    .from(Ref::self_())
568                    .to(&driver_manager),
569            )
570            .await?;
571
572        realm
573            .add_route(
574                Route::new()
575                    .capability(
576                        Capability::configuration(
577                            "fuchsia.driver.manager.SetRootDriverHostCritical",
578                        )
579                        .optional(),
580                    )
581                    .capability(
582                        Capability::configuration("fuchsia.driver.manager.SuspendTimeoutFallback")
583                            .optional(),
584                    )
585                    .from(Ref::void())
586                    .to(&driver_manager),
587            )
588            .await?;
589
590        realm
591            .add_route(
592                Route::new()
593                    .capability(Capability::configuration(
594                        "fuchsia.driver.testrealm.TunnelBootItems",
595                    ))
596                    .capability(Capability::configuration("fuchsia.driver.testrealm.BoardName"))
597                    .capability(Capability::configuration("fuchsia.driver.testrealm.PlatformVid"))
598                    .capability(Capability::configuration("fuchsia.driver.testrealm.PlatformPid"))
599                    .from(Ref::self_())
600                    .to(&dtr_support),
601            )
602            .await?;
603
604        realm
605            .add_route(
606                Route::new()
607                    .capability(Capability::configuration("fuchsia.power.WaitForSuspendingToken"))
608                    .from(Ref::self_())
609                    .to(&driver_manager),
610            )
611            .await?;
612
613        realm
614            .add_route(
615                Route::new()
616                    .capability(
617                        Capability::configuration("fuchsia.platform.bus.SoftwareDeviceNames")
618                            .optional(),
619                    )
620                    .capability(
621                        Capability::configuration("fuchsia.platform.bus.SoftwareDeviceIds")
622                            .optional(),
623                    )
624                    .from(software_devs_src)
625                    .to(&boot_drivers),
626            )
627            .await?;
628
629        // Dynamic routes to the driver framework children.
630        realm
631            .add_route(
632                Route::new()
633                    .capability(Capability::protocol_by_name("fuchsia.driver.test.Internal"))
634                    .from(&driver_test_internal)
635                    .to(&fake_resolver),
636            )
637            .await?;
638
639        // Routes from the driver framework children out to the test.
640        self.add_route(
641            Route::new()
642                .capability(Capability::directory("dev-class"))
643                .capability(Capability::directory("dev-topological"))
644                .capability(Capability::protocol_by_name(
645                    "fuchsia.driver.registrar.DriverRegistrar",
646                ))
647                .capability(Capability::protocol_by_name("fuchsia.driver.development.Manager"))
648                .capability(Capability::protocol_by_name(
649                    "fuchsia.driver.framework.CompositeNodeManager",
650                ))
651                .capability(Capability::protocol_by_name("fuchsia.system.state.Administrator"))
652                .from(&realm)
653                .to(Ref::parent()),
654        )
655        .await?;
656        // LINT.ThenChange(/sdk/lib/driver_test_realm/realm_builder/cpp/builder.cc)
657        Ok(&self)
658    }
659}
660
661#[async_trait::async_trait]
662pub trait DriverTestRealmInstance {
663    /// Connect to the /dev/ directory hosted by  DriverTestRealm in this Instance.
664    fn driver_test_realm_connect_to_dev(&self) -> Result<fio::DirectoryProxy>;
665
666    /// Waits for the driver manager boot up logic to complete. This will ensure all
667    /// in-progress binds complete and indicates its safe to proceed with the test
668    /// or tear down the test realm with no errors.
669    async fn wait_for_bootup(&self) -> Result<()>;
670
671    /// Waits for the node matching the given moniker.
672    async fn wait_for_node(&self, moniker: &str) -> Result<fdd::NodeInfo>;
673}
674
675#[async_trait::async_trait]
676impl DriverTestRealmInstance for RealmInstance {
677    fn driver_test_realm_connect_to_dev(&self) -> Result<fio::DirectoryProxy> {
678        fuchsia_fs::directory::open_directory_async(
679            self.root.get_exposed_dir(),
680            "dev-topological",
681            fio::Flags::empty(),
682        )
683        .map_err(Into::into)
684    }
685
686    async fn wait_for_bootup(&self) -> Result<()> {
687        let manager: fdd::ManagerProxy = self.root.connect_to_protocol_at_exposed_dir()?;
688        manager.wait_for_bootup().await?;
689        Ok(())
690    }
691
692    async fn wait_for_node(&self, moniker: &str) -> Result<fdd::NodeInfo> {
693        let manager: fdd::ManagerProxy = self.root.connect_to_protocol_at_exposed_dir()?;
694        loop {
695            let (iterator, iterator_server) =
696                fidl::endpoints::create_proxy::<fdd::NodeInfoIteratorMarker>();
697            manager.get_node_info(&[moniker.to_string()], iterator_server, true)?;
698            let next = iterator.get_next().await;
699            if let Ok(nodes) = next
700                && !nodes.is_empty()
701                && nodes[0].moniker == Some(moniker.to_string())
702            {
703                return Ok(nodes[0].clone());
704            }
705        }
706    }
707}