Skip to main content

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