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.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().into(),
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().into(),
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().into(),
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_iter().map(FlyStr::new).collect::<Box<[_]>>(),
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_iter().map(FlyStr::new).collect::<Box<[_]>>(),
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.into(),
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(
534                                names.into_iter().map(FlyStr::new).collect::<Box<[_]>>(),
535                            ),
536                        ),
537                    }))
538                    .await?;
539                realm
540                    .add_capability(cm_rust::CapabilityDecl::Config(cm_rust::ConfigurationDecl {
541                        name: "fuchsia.platform.bus.SoftwareDeviceIds".parse()?,
542                        value: cm_rust::ConfigValue::Vector(
543                            cm_rust::ConfigVectorValue::Uint32Vector(ids.into()),
544                        ),
545                    }))
546                    .await?;
547                Ref::self_()
548            }
549            None => Ref::void(),
550        };
551
552        // Config routes.
553        realm
554            .add_route(
555                Route::new()
556                    .capability(Capability::configuration("fuchsia.driver.BindEager"))
557                    .capability(Capability::configuration("fuchsia.driver.DisabledDrivers"))
558                    .capability(Capability::configuration(
559                        "fuchsia.driver.index.StopOnIdleTimeoutMillis",
560                    ))
561                    .from(Ref::self_())
562                    .to(&driver_index),
563            )
564            .await?;
565
566        realm
567            .add_route(
568                Route::new()
569                    .capability(Capability::configuration("fuchsia.driver.manager.RootDriver"))
570                    .from(Ref::self_())
571                    .to(&driver_manager),
572            )
573            .await?;
574
575        realm
576            .add_route(
577                Route::new()
578                    .capability(
579                        Capability::configuration(
580                            "fuchsia.driver.manager.SetRootDriverHostCritical",
581                        )
582                        .optional(),
583                    )
584                    .capability(
585                        Capability::configuration("fuchsia.driver.manager.SuspendTimeoutFallback")
586                            .optional(),
587                    )
588                    .from(Ref::void())
589                    .to(&driver_manager),
590            )
591            .await?;
592
593        realm
594            .add_route(
595                Route::new()
596                    .capability(Capability::configuration(
597                        "fuchsia.driver.testrealm.TunnelBootItems",
598                    ))
599                    .capability(Capability::configuration("fuchsia.driver.testrealm.BoardName"))
600                    .capability(Capability::configuration("fuchsia.driver.testrealm.PlatformVid"))
601                    .capability(Capability::configuration("fuchsia.driver.testrealm.PlatformPid"))
602                    .from(Ref::self_())
603                    .to(&dtr_support),
604            )
605            .await?;
606
607        realm
608            .add_route(
609                Route::new()
610                    .capability(Capability::configuration("fuchsia.power.WaitForSuspendingToken"))
611                    .from(Ref::self_())
612                    .to(&driver_manager),
613            )
614            .await?;
615
616        realm
617            .add_route(
618                Route::new()
619                    .capability(
620                        Capability::configuration("fuchsia.platform.bus.SoftwareDeviceNames")
621                            .optional(),
622                    )
623                    .capability(
624                        Capability::configuration("fuchsia.platform.bus.SoftwareDeviceIds")
625                            .optional(),
626                    )
627                    .from(software_devs_src)
628                    .to(&boot_drivers),
629            )
630            .await?;
631
632        // Dynamic routes to the driver framework children.
633        realm
634            .add_route(
635                Route::new()
636                    .capability(Capability::protocol_by_name("fuchsia.driver.test.Internal"))
637                    .from(&driver_test_internal)
638                    .to(&fake_resolver),
639            )
640            .await?;
641
642        // Routes from the driver framework children out to the test.
643        self.add_route(
644            Route::new()
645                .capability(Capability::directory("dev-class"))
646                .capability(Capability::directory("dev-topological"))
647                .capability(Capability::protocol_by_name(
648                    "fuchsia.driver.registrar.DriverRegistrar",
649                ))
650                .capability(Capability::protocol_by_name("fuchsia.driver.development.Manager"))
651                .capability(Capability::protocol_by_name(
652                    "fuchsia.driver.framework.CompositeNodeManager",
653                ))
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}