1use assert_matches::assert_matches;
6use diagnostics_assertions::assert_data_tree;
7use diagnostics_reader::ArchiveReader;
8use disk_builder::Disk;
9use fake_keymint::FakeKeymint;
10use fidl::endpoints::{ServiceMarker as _, create_proxy};
11use fidl_fuchsia_boot as fboot;
12use fidl_fuchsia_driver_test as fdt;
13use fidl_fuchsia_driver_token as ftoken;
14use fidl_fuchsia_feedback as ffeedback;
15use fidl_fuchsia_fshost_fxfsprovisioner as ffxfsprovisioner;
16use fidl_fuchsia_fxfs::{BlobReaderMarker, CryptManagementProxy, CryptProxy, KeyPurpose};
17use fidl_fuchsia_hardware_block_volume as fvolume;
18use fidl_fuchsia_hardware_inlineencryption as finline;
19use fidl_fuchsia_hardware_ramdisk as framdisk;
20use fidl_fuchsia_io as fio;
21use fidl_fuchsia_security_keymint as fkeymint;
22use fidl_fuchsia_storage_block as fblock;
23use fidl_fuchsia_storage_partitions as fpartitions;
24use fuchsia_async::{self as fasync, TimeoutExt as _};
25use fuchsia_component::client::{
26 connect_to_named_protocol_at_dir_root, connect_to_protocol_at_dir_root,
27};
28use fuchsia_component_test::{Capability, ChildOptions, RealmBuilder, RealmInstance, Ref, Route};
29use fuchsia_driver_test::{DriverTestRealmBuilder, DriverTestRealmInstance};
30use futures::channel::mpsc;
31use futures::{FutureExt as _, StreamExt as _};
32use ramdevice_client::{RamdiskClient, RamdiskClientBuilder};
33use std::pin::pin;
34use std::sync::Arc;
35use std::time::Duration;
36use test_vmo_backed_block_server::VmoBackedServer;
37
38pub mod disk_builder;
39mod mocks;
40
41pub use disk_builder::write_blob;
42pub use fshost_assembly_config::{BlockDeviceConfig, BlockDeviceIdentifiers, BlockDeviceParent};
43
44pub const VFS_TYPE_BLOBFS: u32 = 0x9e694d21;
45pub const VFS_TYPE_MINFS: u32 = 0x6e694d21;
46pub const VFS_TYPE_MEMFS: u32 = 0x3e694d21;
47pub const VFS_TYPE_FXFS: u32 = 0x73667866;
48pub const VFS_TYPE_F2FS: u32 = 0xfe694d21;
49pub const STARNIX_VOLUME_NAME: &str = "starnix_volume";
50
51const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
52
53async fn with_timeout<F: std::future::Future>(fut: F, name: impl Into<String>) -> F::Output {
54 let name = name.into();
55 fut.on_timeout(DEFAULT_TIMEOUT, move || panic!("{name} timed out after {DEFAULT_TIMEOUT:?}"))
56 .await
57}
58
59pub fn round_down<
60 T: Into<U>,
61 U: Copy + std::ops::Rem<U, Output = U> + std::ops::Sub<U, Output = U>,
62>(
63 offset: U,
64 block_size: T,
65) -> U {
66 let block_size = block_size.into();
67 offset - offset % block_size
68}
69
70pub struct TestFixtureBuilder {
71 no_fuchsia_boot: bool,
72 disk: Option<Disk>,
73 extra_disks: Vec<Disk>,
74 fshost: fshost_testing::FshostBuilder,
75 zbi_ramdisk: Option<disk_builder::DiskBuilder>,
76 force_fxfs_provisioner_failure: bool,
77 keymint: std::sync::Arc<FakeKeymint>,
78 crypt_policy: crypt_policy::Policy,
79 simulated_gpt: Option<Arc<VmoBackedServer>>,
80}
81
82impl TestFixtureBuilder {
83 pub fn new(fshost_component_name: &'static str) -> Self {
84 Self {
85 no_fuchsia_boot: false,
86 disk: None,
87 extra_disks: Vec::new(),
88 fshost: fshost_testing::FshostBuilder::new(fshost_component_name),
89 zbi_ramdisk: None,
90 force_fxfs_provisioner_failure: false,
91 keymint: std::sync::Arc::new(FakeKeymint::default()),
92 crypt_policy: crypt_policy::Policy::Null,
93 simulated_gpt: None,
94 }
95 }
96
97 pub fn fshost(&mut self) -> &mut fshost_testing::FshostBuilder {
98 &mut self.fshost
99 }
100
101 pub fn keymint(&mut self) -> std::sync::Arc<FakeKeymint> {
102 self.keymint.clone()
103 }
104
105 pub fn with_keymint_instance(mut self, keymint: std::sync::Arc<FakeKeymint>) -> Self {
106 self.keymint = keymint.clone();
107 if let Some(Disk::Builder(ref mut disk_builder)) = self.disk {
108 disk_builder.with_keymint_instance(keymint.clone());
109 }
110 for disk in &mut self.extra_disks {
111 if let Disk::Builder(disk_builder) = disk {
112 disk_builder.with_keymint_instance(keymint.clone());
113 }
114 }
115 self
116 }
117
118 pub fn with_disk(&mut self) -> &mut disk_builder::DiskBuilder {
119 self.disk = Some(Disk::Builder(disk_builder::DiskBuilder::new()));
120 self.disk
121 .as_mut()
122 .unwrap()
123 .builder()
124 .with_crypt_policy(self.crypt_policy)
125 .with_keymint_instance(self.keymint.clone());
126 self.disk.as_mut().unwrap().builder()
127 }
128
129 pub fn with_extra_disk(&mut self) -> &mut disk_builder::DiskBuilder {
130 self.extra_disks.push(Disk::Builder(disk_builder::DiskBuilder::new()));
131 self.extra_disks
132 .last_mut()
133 .unwrap()
134 .builder()
135 .with_crypt_policy(self.crypt_policy)
136 .with_keymint_instance(self.keymint.clone());
137 self.extra_disks.last_mut().unwrap().builder()
138 }
139
140 pub fn with_uninitialized_disk(mut self) -> Self {
141 self.disk = Some(Disk::Builder(disk_builder::DiskBuilder::uninitialized()));
142 self
143 }
144
145 pub fn with_disk_from(mut self, disk: Disk) -> Self {
146 self.disk = Some(disk);
147 self
148 }
149
150 pub fn with_simulated_gpt(mut self, server: Arc<VmoBackedServer>) -> Self {
151 self.simulated_gpt = Some(server);
152 self
153 }
154
155 pub fn with_zbi_ramdisk(&mut self) -> &mut disk_builder::DiskBuilder {
156 self.zbi_ramdisk = Some(disk_builder::DiskBuilder::new());
157 self.zbi_ramdisk.as_mut().unwrap()
158 }
159
160 pub fn no_fuchsia_boot(mut self) -> Self {
161 self.no_fuchsia_boot = true;
162 self
163 }
164
165 pub fn with_device_config(mut self, device_config: Vec<BlockDeviceConfig>) -> Self {
166 self.fshost.set_device_config(device_config);
167 self
168 }
169
170 pub fn with_crypt_policy(mut self, policy: crypt_policy::Policy) -> Self {
171 self.fshost.set_crypt_policy(policy);
172 self.crypt_policy = policy;
173 if let Some(Disk::Builder(ref mut disk_builder)) = self.disk {
174 disk_builder.with_crypt_policy(policy);
175 }
176 for disk in &mut self.extra_disks {
177 if let Disk::Builder(disk_builder) = disk {
178 disk_builder.with_crypt_policy(policy);
179 }
180 }
181 self
182 }
183
184 pub fn force_fxfs_provisioner_failure(mut self) -> Self {
185 self.force_fxfs_provisioner_failure = true;
186 self
187 }
188
189 pub async fn build(self) -> TestFixture {
190 let builder = RealmBuilder::new().await.unwrap();
191 let fshost = self.fshost.build(&builder).await;
192 builder
194 .add_route(
195 Route::new()
196 .capability(Capability::protocol::<finline::DeviceMarker>())
197 .from(&fshost)
198 .to(Ref::parent()),
199 )
200 .await
201 .unwrap();
202
203 builder
204 .add_route(
205 Route::new()
206 .capability(Capability::protocol::<fidl_fuchsia_component::RealmMarker>())
207 .from(Ref::framework())
208 .to(Ref::parent()),
209 )
210 .await
211 .unwrap();
212
213 let maybe_zbi_vmo = match self.zbi_ramdisk {
214 Some(disk_builder) => Some(disk_builder.build_as_zbi_ramdisk().await),
215 None => None,
216 };
217 let (tx, crash_reports) = mpsc::channel(32);
218 let mocks = mocks::new_mocks(
219 maybe_zbi_vmo,
220 tx,
221 self.force_fxfs_provisioner_failure,
222 self.keymint.clone(),
223 self.simulated_gpt.clone(),
224 );
225
226 let mocks = builder
227 .add_local_child("mocks", move |h| mocks(h).boxed(), ChildOptions::new())
228 .await
229 .unwrap();
230 builder
231 .add_route(
232 Route::new()
233 .capability(Capability::protocol::<fkeymint::SealingKeysMarker>())
234 .capability(Capability::protocol::<fkeymint::AdminMarker>())
235 .from(&mocks)
236 .to(Ref::parent()),
237 )
238 .await
239 .unwrap();
240 builder
241 .add_route(
242 Route::new()
243 .capability(Capability::protocol::<ffeedback::CrashReporterMarker>())
244 .capability(Capability::protocol::<ffxfsprovisioner::FxfsProvisionerMarker>())
245 .capability(Capability::protocol::<fkeymint::SealingKeysMarker>())
246 .capability(Capability::protocol::<fkeymint::AdminMarker>())
247 .capability(Capability::protocol::<ftoken::NodeBusTopologyMarker>())
248 .from(&mocks)
249 .to(&fshost),
250 )
251 .await
252 .unwrap();
253 builder
254 .add_route(
255 Route::new()
256 .capability(Capability::service::<fvolume::ServiceMarker>())
257 .from(&mocks)
258 .to(&fshost),
259 )
260 .await
261 .unwrap();
262 if !self.no_fuchsia_boot {
263 builder
264 .add_route(
265 Route::new()
266 .capability(Capability::protocol::<fboot::ArgumentsMarker>())
267 .capability(Capability::protocol::<fboot::ItemsMarker>())
268 .from(&mocks)
269 .to(&fshost),
270 )
271 .await
272 .unwrap();
273 }
274
275 builder
276 .add_route(
277 Route::new()
278 .capability(Capability::dictionary("diagnostics"))
279 .from(Ref::parent())
280 .to(&fshost),
281 )
282 .await
283 .unwrap();
284
285 let dtr_exposes = vec![
286 fidl_fuchsia_component_test::Capability::Service(
287 fidl_fuchsia_component_test::Service {
288 name: Some("fuchsia.hardware.ramdisk.Service".to_owned()),
289 ..Default::default()
290 },
291 ),
292 fidl_fuchsia_component_test::Capability::Service(
293 fidl_fuchsia_component_test::Service {
294 name: Some("fuchsia.hardware.block.volume.Service".to_owned()),
295 ..Default::default()
296 },
297 ),
298 ];
299 builder.driver_test_realm_setup().await.unwrap();
300 builder.driver_test_realm_add_dtr_exposes(&dtr_exposes).await.unwrap();
301 builder
302 .add_route(
303 Route::new()
304 .capability(Capability::directory("dev-topological").rights(fio::R_STAR_DIR))
305 .capability(Capability::service::<fvolume::ServiceMarker>())
306 .from(Ref::child(fuchsia_driver_test::COMPONENT_NAME))
307 .to(&fshost),
308 )
309 .await
310 .unwrap();
311 builder
312 .add_route(
313 Route::new()
314 .capability(
315 Capability::directory("dev-class")
316 .rights(fio::R_STAR_DIR)
317 .subdir("block")
318 .as_("dev-class-block"),
319 )
320 .from(Ref::child(fuchsia_driver_test::COMPONENT_NAME))
321 .to(Ref::parent()),
322 )
323 .await
324 .unwrap();
325
326 let realm = builder.build().await.unwrap();
327 let realm_proxy = connect_to_protocol_at_dir_root::<fidl_fuchsia_component::RealmMarker>(
328 realm.root.get_exposed_dir(),
329 )
330 .expect("failed to connect to Realm");
331 let (controller, controller_server_end) =
332 create_proxy::<fidl_fuchsia_component::ControllerMarker>();
333 realm_proxy
334 .open_controller(
335 &fidl_fuchsia_component_decl::ChildRef {
336 name: "test-fshost".to_string(),
337 collection: None,
338 },
339 controller_server_end,
340 )
341 .await
342 .expect("failed to open controller")
343 .expect("open controller error");
344 let (execution_controller, execution_controller_server_end) =
345 create_proxy::<fidl_fuchsia_component::ExecutionControllerMarker>();
346 controller
347 .start(
348 fidl_fuchsia_component::StartChildArgs::default(),
349 execution_controller_server_end,
350 )
351 .await
352 .expect("failed to start fshost")
353 .expect("start fshost error");
354
355 let mut fixture = TestFixture {
356 realm,
357 ramdisks: Vec::new(),
358 main_disk: None,
359 crash_reports,
360 execution_controller,
361 torn_down: TornDown(false),
362 };
363
364 log::info!(
365 realm_name:? = fixture.realm.root.child_name();
366 "built new test realm",
367 );
368
369 fixture
370 .realm
371 .driver_test_realm_start(fdt::RealmArgs {
372 root_driver: Some("fuchsia-boot:///platform-bus#meta/platform-bus.cm".to_owned()),
373 dtr_exposes: Some(dtr_exposes),
374 software_devices: Some(vec![
375 fdt::SoftwareDevice {
376 device_name: "ram-disk".to_string(),
377 device_id: bind_fuchsia_platform::BIND_PLATFORM_DEV_DID_RAM_DISK,
378 },
379 fdt::SoftwareDevice {
380 device_name: "ram-nand".to_string(),
381 device_id: bind_fuchsia_platform::BIND_PLATFORM_DEV_DID_RAM_NAND,
382 },
383 ]),
384 ..Default::default()
385 })
386 .await
387 .unwrap();
388
389 for disk in self.extra_disks.into_iter() {
396 fixture.add_disk(disk).await;
397 }
398 if let Some(disk) = self.disk {
399 fixture.add_main_disk(disk).await;
400 }
401
402 fixture
403 }
404}
405
406struct TornDown(bool);
409
410impl Drop for TornDown {
411 fn drop(&mut self) {
412 assert!(self.0, "fixture.tear_down() must be called");
415 }
416}
417
418pub struct TestFixture {
419 pub realm: RealmInstance,
420 pub ramdisks: Vec<RamdiskClient>,
421 pub main_disk: Option<Disk>,
422 pub crash_reports: mpsc::Receiver<ffeedback::CrashReport>,
423 pub execution_controller: fidl_fuchsia_component::ExecutionControllerProxy,
424 torn_down: TornDown,
425}
426
427impl TestFixture {
428 pub async fn tear_down(mut self) -> Option<Disk> {
429 log::info!(realm_name:? = self.realm.root.child_name(); "tearing down");
430 let disk = self.main_disk.take();
431 assert_matches!(self.crash_reports.try_recv(), Err(_));
434 self.realm.destroy().await.unwrap();
435 self.torn_down.0 = true;
436 disk
437 }
438
439 pub fn exposed_dir(&self) -> &fio::DirectoryProxy {
440 self.realm.root.get_exposed_dir()
441 }
442
443 pub fn dir(&self, dir: &str, flags: fio::Flags) -> fio::DirectoryProxy {
444 let (dev, server) = create_proxy::<fio::DirectoryMarker>();
445 let flags = flags | fio::Flags::PROTOCOL_DIRECTORY;
446 self.realm
447 .root
448 .get_exposed_dir()
449 .open(dir, flags, &fio::Options::default(), server.into_channel())
450 .expect("open failed");
451 dev
452 }
453
454 pub async fn check_fs_type(&self, dir: &str, fs_type: u32) {
455 let (status, info) = with_timeout(
456 self.dir(dir, fio::PERM_READABLE).query_filesystem(),
457 format!("check_fs_type({dir})"),
458 )
459 .await
460 .expect("query failed");
461 assert_eq!(status, zx::sys::ZX_OK);
462 assert!(info.is_some());
463 let info_type = info.unwrap().fs_type;
464 assert_eq!(info_type, fs_type, "{:#08x} != {:#08x}", info_type, fs_type);
465 }
466
467 pub async fn check_test_blob(&self) {
468 with_timeout(
469 async {
470 let expected_blob_hash = disk_builder::test_blob_hash();
471 let reader = connect_to_protocol_at_dir_root::<BlobReaderMarker>(
472 self.realm.root.get_exposed_dir(),
473 )
474 .expect("failed to connect to the BlobReader");
475 let _vmo = reader
476 .get_vmo(&expected_blob_hash.into())
477 .await
478 .expect("blob get_vmo fidl error")
479 .unwrap_or_else(|e| match zx::Status::err_from_raw(e) {
480 zx::Status::NOT_FOUND => panic!("Test blob not found - blobfs lost data!"),
481 s => panic!("Error while opening test blob vmo: {s}"),
482 });
483 },
484 "check_test_blob",
485 )
486 .await
487 }
488
489 pub async fn check_test_data_file(&self) {
492 with_timeout(
493 async {
494 let (file, server) = create_proxy::<fio::NodeMarker>();
495 self.dir("data", fio::PERM_READABLE)
496 .open(
497 ".testdata",
498 fio::PERM_READABLE,
499 &fio::Options::default(),
500 server.into_channel(),
501 )
502 .expect("open failed");
503 file.get_attributes(fio::NodeAttributesQuery::empty())
504 .await
505 .expect("Fidl transport error on get_attributes()")
506 .expect("get_attr failed - data was probably deleted!");
507
508 let data = self.dir("data", fio::PERM_READABLE);
509 fuchsia_fs::directory::open_file(&data, ".testdata", fio::PERM_READABLE)
510 .await
511 .unwrap();
512
513 fuchsia_fs::directory::open_directory(&data, "ssh", fio::PERM_READABLE)
514 .await
515 .unwrap();
516 fuchsia_fs::directory::open_directory(&data, "ssh/config", fio::PERM_READABLE)
517 .await
518 .unwrap();
519 fuchsia_fs::directory::open_directory(&data, "problems", fio::PERM_READABLE)
520 .await
521 .unwrap();
522
523 let authorized_keys = fuchsia_fs::directory::open_file(
524 &data,
525 "ssh/authorized_keys",
526 fio::PERM_READABLE,
527 )
528 .await
529 .unwrap();
530 assert_eq!(
531 &fuchsia_fs::file::read_to_string(&authorized_keys).await.unwrap(),
532 "public key!"
533 );
534 },
535 "check_test_data_file",
536 )
537 .await
538 }
539
540 pub async fn check_test_data_file_absent(&self) {
543 let err = with_timeout(
544 fuchsia_fs::directory::open_file(
545 &self.dir("data", fio::PERM_READABLE),
546 ".testdata",
547 fio::PERM_READABLE,
548 ),
549 "check_test_data_file_absent",
550 )
551 .await
552 .expect_err("open_file failed");
553 assert!(err.is_not_found_error());
554 }
555
556 pub async fn add_main_disk(&mut self, disk: Disk) {
557 assert!(self.main_disk.is_none());
558 let (vmo, type_guid) = disk.into_vmo_and_type_guid().await;
559 let vmo_clone =
560 vmo.create_child(zx::VmoChildOptions::SLICE, 0, vmo.get_size().unwrap()).unwrap();
561
562 self.add_ramdisk(vmo, type_guid).await;
563 self.main_disk = Some(Disk::Prebuilt(vmo_clone, type_guid));
564 }
565
566 pub async fn add_disk(&mut self, disk: Disk) {
567 let (vmo, type_guid) = disk.into_vmo_and_type_guid().await;
568 self.add_ramdisk(vmo, type_guid).await;
569 }
570
571 async fn add_ramdisk(&mut self, vmo: zx::Vmo, type_guid: Option<[u8; 16]>) {
572 let mut ramdisk_builder = RamdiskClientBuilder::new_with_vmo(vmo, Some(512))
573 .publish()
574 .ramdisk_service(self.dir(framdisk::ServiceMarker::SERVICE_NAME, fio::PERM_READABLE));
575 if let Some(guid) = type_guid {
576 ramdisk_builder = ramdisk_builder.guid(guid);
577 }
578 let mut ramdisk = pin!(ramdisk_builder.build().fuse());
579
580 let ramdisk = futures::select_biased!(
581 res = ramdisk => res,
582 _ = fasync::Timer::new(Duration::from_secs(120))
583 .fuse() => panic!("Timed out waiting for RamdiskClient"),
584 )
585 .unwrap();
586 self.ramdisks.push(ramdisk);
587 }
588
589 pub fn connect_to_crypt(&self) -> CryptProxy {
590 self.realm
591 .root
592 .connect_to_protocol_at_exposed_dir()
593 .expect("connect_to_protocol_at_exposed_dir failed for the Crypt protocol")
594 }
595
596 pub async fn setup_starnix_crypt(&self) -> (CryptProxy, CryptManagementProxy) {
597 let crypt_management: CryptManagementProxy =
598 self.realm.root.connect_to_protocol_at_exposed_dir().expect(
599 "connect_to_protocol_at_exposed_dir failed for the CryptManagement protocol",
600 );
601 let crypt = self
602 .realm
603 .root
604 .connect_to_protocol_at_exposed_dir()
605 .expect("connect_to_protocol_at_exposed_dir failed for the Crypt protocol");
606 let key = vec![0xABu8; 32];
607 crypt_management
608 .add_wrapping_key(&u128::to_le_bytes(0), key.as_slice())
609 .await
610 .expect("fidl transport error")
611 .expect("add wrapping key failed");
612 crypt_management
613 .add_wrapping_key(&u128::to_le_bytes(1), key.as_slice())
614 .await
615 .expect("fidl transport error")
616 .expect("add wrapping key failed");
617 crypt_management
618 .set_active_key(KeyPurpose::Data, &u128::to_le_bytes(0))
619 .await
620 .expect("fidl transport error")
621 .expect("set metadata key failed");
622 crypt_management
623 .set_active_key(KeyPurpose::Metadata, &u128::to_le_bytes(1))
624 .await
625 .expect("fidl transport error")
626 .expect("set metadata key failed");
627 (crypt, crypt_management)
628 }
629
630 pub async fn wait_for_crash_reports(
633 &mut self,
634 count: usize,
635 expected_program: &'_ str,
636 expected_signature: &'_ str,
637 ) {
638 log::info!("Waiting for {count} crash reports");
639 for _ in 0..count {
640 let report = self.crash_reports.next().await.expect("Sender closed");
641 assert_eq!(report.program_name.as_deref(), Some(expected_program));
642 assert_eq!(report.crash_signature.as_deref(), Some(expected_signature));
643 }
644 if count > 0 {
645 let selector =
646 format!("realm_builder\\:{}/test-fshost:root", self.realm.root.child_name());
647 log::info!("Checking inspect for corruption event, selector={selector}");
648 let tree = ArchiveReader::inspect()
649 .add_selector(selector)
650 .snapshot()
651 .await
652 .unwrap()
653 .into_iter()
654 .next()
655 .and_then(|result| result.payload)
656 .expect("expected one inspect hierarchy");
657
658 let format = || expected_program.to_string();
659 if expected_signature.contains("unseal-error") {
660 assert_data_tree!(tree, root: contains {
661 keymint_unseal_failure_events: contains {
662 format() => 1u64,
663 }
664 });
665 } else {
666 assert_data_tree!(tree, root: contains {
667 corruption_events: contains {
668 format() => 1u64,
669 }
670 });
671 }
672 }
673 }
674
675 pub async fn check_system_partitions(&self, mut expected: Vec<&str>) {
677 with_timeout(
678 async {
679 let partitions =
680 self.dir(fpartitions::PartitionServiceMarker::SERVICE_NAME, fio::PERM_READABLE);
681 let entries = fuchsia_fs::directory::readdir(&partitions)
682 .await
683 .expect("Failed to read partitions");
684
685 assert_eq!(entries.len(), expected.len());
686
687 let mut found_partition_labels = Vec::new();
688 for entry in entries {
689 let endpoint_name = format!("{}/volume", entry.name);
690 let volume = connect_to_named_protocol_at_dir_root::<fblock::BlockMarker>(
691 &partitions,
692 &endpoint_name,
693 )
694 .expect("failed to connect to named protocol at dir root");
695 let (raw_status, label) =
696 volume.get_name().await.expect("failed to call get_name");
697 zx::Status::ok(raw_status).expect("get_name status failed");
698 found_partition_labels
699 .push(label.expect("partition label expected to be some value"));
700 }
701 found_partition_labels.sort();
702 expected.sort();
703 assert_eq!(found_partition_labels, expected);
704 },
705 "check_system_partitions",
706 )
707 .await
708 }
709}