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