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 cm_rust::FidlIntoNative;
7use fidl_fuchsia_component_decl as fdecl;
8use fidl_fuchsia_component_test as ftest;
9use fidl_fuchsia_driver_development as fdd;
10use fidl_fuchsia_driver_test as fdt;
11use fidl_fuchsia_io as fio;
12use flyweights::FlyStr;
13use fuchsia_async as fasync;
14use fuchsia_component::server::ServiceFs;
15use fuchsia_component_test::{
16    Capability, ChildOptions, ChildRef, CollectionRef, LocalComponentHandles, RealmBuilder,
17    RealmInstance, Ref, Route,
18};
19use futures::{StreamExt, TryStreamExt};
20use std::sync::Arc;
21use zx::AsHandleRef;
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.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().into(),
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().into(),
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().into(),
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_iter().map(FlyStr::new).collect::<Box<[_]>>(),
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_iter().map(FlyStr::new).collect::<Box<[_]>>(),
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.into(),
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(
533                                names.into_iter().map(FlyStr::new).collect::<Box<[_]>>(),
534                            ),
535                        ),
536                    }))
537                    .await?;
538                realm
539                    .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
540                        name: "fuchsia.platform.bus.SoftwareDeviceIds".parse()?,
541                        value: cm_rust::ConfigValue::Vector(
542                            cm_rust::ConfigVectorValue::Uint32Vector(ids.into()),
543                        ),
544                    }))
545                    .await?;
546                Ref::self_()
547            }
548            None => Ref::void(),
549        };
550
551        // Config routes.
552        realm
553            .add_route(
554                Route::new()
555                    .capability(Capability::configuration("fuchsia.driver.BindEager"))
556                    .capability(Capability::configuration("fuchsia.driver.DisabledDrivers"))
557                    .capability(Capability::configuration(
558                        "fuchsia.driver.index.StopOnIdleTimeoutMillis",
559                    ))
560                    .from(Ref::self_())
561                    .to(&driver_index),
562            )
563            .await?;
564
565        realm
566            .add_route(
567                Route::new()
568                    .capability(Capability::configuration("fuchsia.driver.manager.RootDriver"))
569                    .from(Ref::self_())
570                    .to(&driver_manager),
571            )
572            .await?;
573
574        realm
575            .add_route(
576                Route::new()
577                    .capability(
578                        Capability::configuration(
579                            "fuchsia.driver.manager.SetRootDriverHostCritical",
580                        )
581                        .optional(),
582                    )
583                    .capability(
584                        Capability::configuration("fuchsia.driver.manager.SuspendTimeoutFallback")
585                            .optional(),
586                    )
587                    .from(Ref::void())
588                    .to(&driver_manager),
589            )
590            .await?;
591
592        realm
593            .add_route(
594                Route::new()
595                    .capability(Capability::configuration(
596                        "fuchsia.driver.testrealm.TunnelBootItems",
597                    ))
598                    .capability(Capability::configuration("fuchsia.driver.testrealm.BoardName"))
599                    .capability(Capability::configuration("fuchsia.driver.testrealm.PlatformVid"))
600                    .capability(Capability::configuration("fuchsia.driver.testrealm.PlatformPid"))
601                    .from(Ref::self_())
602                    .to(&dtr_support),
603            )
604            .await?;
605
606        realm
607            .add_route(
608                Route::new()
609                    .capability(Capability::configuration("fuchsia.power.WaitForSuspendingToken"))
610                    .from(Ref::self_())
611                    .to(&driver_manager),
612            )
613            .await?;
614
615        realm
616            .add_route(
617                Route::new()
618                    .capability(
619                        Capability::configuration("fuchsia.platform.bus.SoftwareDeviceNames")
620                            .optional(),
621                    )
622                    .capability(
623                        Capability::configuration("fuchsia.platform.bus.SoftwareDeviceIds")
624                            .optional(),
625                    )
626                    .from(software_devs_src)
627                    .to(&boot_drivers),
628            )
629            .await?;
630
631        // Dynamic routes to the driver framework children.
632        realm
633            .add_route(
634                Route::new()
635                    .capability(Capability::protocol_by_name("fuchsia.driver.test.Internal"))
636                    .from(&driver_test_internal)
637                    .to(&fake_resolver),
638            )
639            .await?;
640
641        // Routes from the driver framework children out to the test.
642        self.add_route(
643            Route::new()
644                .capability(Capability::directory("dev-class"))
645                .capability(Capability::directory("dev-topological"))
646                .capability(Capability::protocol_by_name(
647                    "fuchsia.driver.registrar.DriverRegistrar",
648                ))
649                .capability(Capability::protocol_by_name("fuchsia.driver.development.Manager"))
650                .capability(Capability::protocol_by_name(
651                    "fuchsia.driver.framework.CompositeNodeManager",
652                ))
653                .capability(Capability::protocol_by_name("fuchsia.driver.framework.NodeManager"))
654                .capability(Capability::protocol_by_name("fuchsia.system.state.Administrator"))
655                .from(&realm)
656                .to(Ref::parent()),
657        )
658        .await?;
659        // LINT.ThenChange(/sdk/lib/driver_test_realm/realm_builder/cpp/builder.cc)
660        Ok(&self)
661    }
662}
663
664#[async_trait::async_trait]
665pub trait DriverTestRealmInstance {
666    /// Connect to the /dev/ directory hosted by  DriverTestRealm in this Instance.
667    fn driver_test_realm_connect_to_dev(&self) -> Result<fio::DirectoryProxy>;
668
669    /// Waits for the driver manager boot up logic to complete. This will ensure all
670    /// in-progress binds complete and indicates its safe to proceed with the test
671    /// or tear down the test realm with no errors.
672    async fn wait_for_bootup(&self) -> Result<()>;
673
674    /// Waits for the node matching the given moniker.
675    async fn wait_for_node(&self, moniker: &str) -> Result<fdd::NodeInfo>;
676}
677
678#[async_trait::async_trait]
679impl DriverTestRealmInstance for RealmInstance {
680    fn driver_test_realm_connect_to_dev(&self) -> Result<fio::DirectoryProxy> {
681        fuchsia_fs::directory::open_directory_async(
682            self.root.get_exposed_dir(),
683            "dev-topological",
684            fio::Flags::empty(),
685        )
686        .map_err(Into::into)
687    }
688
689    async fn wait_for_bootup(&self) -> Result<()> {
690        let manager: fdd::ManagerProxy = self.root.connect_to_protocol_at_exposed_dir()?;
691        manager.wait_for_bootup().await?;
692        Ok(())
693    }
694
695    async fn wait_for_node(&self, moniker: &str) -> Result<fdd::NodeInfo> {
696        let manager: fdd::ManagerProxy = self.root.connect_to_protocol_at_exposed_dir()?;
697        loop {
698            let (iterator, iterator_server) =
699                fidl::endpoints::create_proxy::<fdd::NodeInfoIteratorMarker>();
700            manager.get_node_info(&[moniker.to_string()], iterator_server, true)?;
701            let next = iterator.get_next().await;
702            if let Ok(nodes) = next
703                && !nodes.is_empty()
704                && nodes[0].moniker == Some(moniker.to_string())
705            {
706                return Ok(nodes[0].clone());
707            }
708        }
709    }
710}