| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use {super::*, pretty_assertions::assert_eq}; |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn fails_on_paver_connect_error() { |
| let env = TestEnv::builder().unregister_protocol(Protocol::Paver).build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake_zbi"); |
| |
| let result = env.run_update().await; |
| assert!(result.is_err(), "system updater succeeded when it should fail"); |
| |
| // Appmgr will close the paver service channel when it is unable to forward the channel to any |
| // implementation of that protocol, but it is a race condition as to whether or not the system |
| // updater will be able to send the requests to open the data sink and boot manager connections |
| // before that happens. So, the update attempt will either fail very early or when it attempts |
| // to query the active configuration. |
| let interactions = env.take_interactions(); |
| assert!( |
| interactions == &[] |
| || interactions == &[Gc, PackageResolve(UPDATE_PKG_URL.to_string()), Gc, BlobfsSync,], |
| "expected early failure or failure while querying active configuration. Got {:#?}", |
| interactions |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn fails_on_image_write_error() { |
| let env = TestEnv::builder() |
| .paver_service(|builder| { |
| builder.call_hook(|event| match event { |
| PaverEvent::WriteAsset { .. } => Status::INTERNAL, |
| _ => Status::OK, |
| }) |
| }) |
| .build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake_zbi"); |
| |
| let result = env.run_update().await; |
| assert!(result.is_err(), "system updater succeeded when it should fail"); |
| |
| assert_eq!( |
| env.get_ota_metrics().await, |
| OtaMetrics { |
| initiator: metrics::OtaResultAttemptsMetricDimensionInitiator::UserInitiatedCheck |
| as u32, |
| phase: metrics::OtaResultAttemptsMetricDimensionPhase::ImageWrite as u32, |
| status_code: metrics::OtaResultAttemptsMetricDimensionStatusCode::Error as u32, |
| target: "".into(), |
| } |
| ); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| },), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| },), |
| Paver(PaverEvent::QueryCurrentConfiguration,), |
| Paver(PaverEvent::QueryActiveConfiguration,), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake_zbi".to_vec(), |
| },), |
| ] |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn skip_recovery_does_not_write_recovery_or_vbmeta() { |
| let env = TestEnv::builder().build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake zbi") |
| .add_file("zedboot", "new recovery") |
| .add_file("recovery.vbmeta", "new recovery vbmeta"); |
| |
| env.run_update_with_options( |
| UPDATE_PKG_URL, |
| Options { should_write_recovery: false, ..default_options() }, |
| ) |
| .await |
| .expect("success"); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryActiveConfiguration), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake zbi".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn writes_to_both_configs_if_abr_not_supported() { |
| let env = TestEnv::builder() |
| .paver_service(|builder| builder.boot_manager_close_with_epitaph(Status::NOT_SUPPORTED)) |
| .build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake_zbi"); |
| |
| env.run_update().await.expect("success"); |
| |
| assert_eq!( |
| env.get_ota_metrics().await, |
| OtaMetrics { |
| initiator: metrics::OtaResultAttemptsMetricDimensionInitiator::UserInitiatedCheck |
| as u32, |
| phase: metrics::OtaResultAttemptsMetricDimensionPhase::SuccessPendingReboot as u32, |
| status_code: metrics::OtaResultAttemptsMetricDimensionStatusCode::Success as u32, |
| target: "".into(), |
| } |
| ); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel, |
| payload: b"fake_zbi".to_vec(), |
| }), |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake_zbi".to_vec(), |
| }), |
| Paver(PaverEvent::DataSinkFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| // If we can't ensure that the current partition == the active partition and the current partition |
| // is healthy, system-updater makes progress |
| async fn updates_even_if_cant_set_active_partition_healthy() { |
| let current_config = paver::Configuration::A; |
| let active_config = paver::Configuration::B; |
| |
| let env = TestEnv::builder() |
| .paver_service(|builder| { |
| builder |
| .call_hook(|event| match event { |
| PaverEvent::SetConfigurationHealthy { .. } => Status::INTERNAL, |
| _ => Status::OK, |
| }) |
| .current_config(current_config) |
| .active_config(active_config) |
| }) |
| .build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake_zbi"); |
| |
| env.run_update().await.expect("success"); |
| |
| assert_eq!( |
| env.get_ota_metrics().await, |
| OtaMetrics { |
| initiator: metrics::OtaResultAttemptsMetricDimensionInitiator::UserInitiatedCheck |
| as u32, |
| phase: metrics::OtaResultAttemptsMetricDimensionPhase::SuccessPendingReboot as u32, |
| status_code: metrics::OtaResultAttemptsMetricDimensionStatusCode::Success as u32, |
| target: "".into(), |
| } |
| ); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryActiveConfiguration), |
| Paver(PaverEvent::QueryConfigurationStatus { configuration: current_config }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: current_config }), |
| Paver(PaverEvent::SetConfigurationHealthy { configuration: current_config }), |
| Paver(PaverEvent::BootManagerFlush), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake_zbi".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| // Assert that when current partition == active partition, we write to the non-current partition. |
| #[fasync::run_singlethreaded(test)] |
| async fn writes_to_non_current_config_if_abr_supported_and_current_config_a() { |
| assert_writes_for_active_equal_to_current( |
| paver::Configuration::A, |
| paver::Configuration::A, |
| paver::Configuration::B, |
| ) |
| .await |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn writes_to_non_current_config_if_abr_supported_and_current_config_b() { |
| assert_writes_for_active_equal_to_current( |
| paver::Configuration::B, |
| paver::Configuration::B, |
| paver::Configuration::A, |
| ) |
| .await |
| } |
| |
| // When current partition != active partition, and current is healthy, |
| // we should reset active to current, and set the active configuration healthy. |
| #[fasync::run_singlethreaded(test)] |
| async fn resets_active_if_active_not_equal_to_current_with_current_a() { |
| assert_resets_active_when_active_not_equal_to_current( |
| paver::Configuration::A, |
| paver::Configuration::B, |
| paver::Configuration::B, |
| ) |
| .await |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn resets_active_if_active_not_equal_to_current_with_current_b() { |
| assert_resets_active_when_active_not_equal_to_current( |
| paver::Configuration::B, |
| paver::Configuration::A, |
| paver::Configuration::A, |
| ) |
| .await |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn does_not_reset_active_if_active_not_equal_to_current_with_current_r() { |
| // Since recovery cannot be the active partition, |
| // we won't run the logic which resets current to active. This should take the "normal" |
| // path and not attempt to reset the active partition, |
| // then write to A, which is the default if we are in recovery. |
| let current_config = paver::Configuration::Recovery; |
| let active_config = paver::Configuration::A; |
| let target_config = paver::Configuration::A; |
| assert_eq!( |
| update_with_current_and_active_configurations(current_config, active_config).await, |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: target_config, |
| asset: paver::Asset::Kernel, |
| payload: b"fake_zbi".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: target_config }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| // If current is not equal to active and current is not healthy |
| // we'll reset active to current, but also NOT set the current partition status to healthy. |
| #[fasync::run_singlethreaded(test)] |
| async fn resets_active_with_unhealthy_current_a() { |
| assert_resets_active_with_unhealthy_current_partition( |
| paver::Configuration::A, |
| paver::Configuration::B, |
| paver::Configuration::B, |
| ) |
| .await |
| } |
| |
| // Test that if current is not equal to active and current is not healthy |
| // we'll reset active to current, but also NOT set the current partition status to healthy. |
| #[fasync::run_singlethreaded(test)] |
| async fn resets_active_with_unhealthy_current_b() { |
| assert_resets_active_with_unhealthy_current_partition( |
| paver::Configuration::B, |
| paver::Configuration::A, |
| paver::Configuration::A, |
| ) |
| .await |
| } |
| |
| // Note that we don't test resetting active with current == Recovery, |
| // because we'll only run the resetting logic if current is either A or B (that flow |
| // is tested in resets_active_if_active_not_equal_to_current_with_current_r) |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn writes_recovery_called_legacy_zedboot() { |
| let env = TestEnv::builder().build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake zbi") |
| .add_file("zedboot", "new recovery"); |
| |
| env.run_update().await.expect("success"); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryActiveConfiguration), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake zbi".to_vec(), |
| }), |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::Recovery, |
| asset: paver::Asset::Kernel, |
| payload: b"new recovery".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| // TODO(fxbug.dev/52356): drop this duplicate test when "zedboot" is no longer allowed/used. |
| #[fasync::run_singlethreaded(test)] |
| async fn writes_recovery() { |
| let env = TestEnv::builder().build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake zbi") |
| .add_file("recovery", "new recovery"); |
| |
| env.run_update().await.expect("success"); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryActiveConfiguration), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake zbi".to_vec(), |
| }), |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::Recovery, |
| asset: paver::Asset::Kernel, |
| payload: b"new recovery".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn writes_recovery_vbmeta() { |
| let env = TestEnv::builder().build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake zbi") |
| .add_file("zedboot", "new recovery") |
| .add_file("recovery.vbmeta", "new recovery vbmeta"); |
| |
| env.run_update().await.expect("success"); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryActiveConfiguration), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake zbi".to_vec(), |
| }), |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::Recovery, |
| asset: paver::Asset::Kernel, |
| payload: b"new recovery".to_vec(), |
| }), |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::Recovery, |
| asset: paver::Asset::VerifiedBootMetadata, |
| payload: b"new recovery vbmeta".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| #[fasync::run_singlethreaded(test)] |
| async fn writes_fuchsia_vbmeta() { |
| let env = TestEnv::builder().build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake zbi") |
| .add_file("fuchsia.vbmeta", "fake zbi vbmeta"); |
| |
| env.run_update().await.expect("success"); |
| |
| assert_eq!( |
| env.take_interactions(), |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: paver::Configuration::A, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryActiveConfiguration), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::Kernel, |
| payload: b"fake zbi".to_vec(), |
| }), |
| Paver(PaverEvent::WriteAsset { |
| configuration: paver::Configuration::B, |
| asset: paver::Asset::VerifiedBootMetadata, |
| payload: b"fake zbi vbmeta".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: paver::Configuration::B }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| // Run an update with a given current and active config, and a customized config_status hook for the boot manager. |
| // Useful for setting a given configuration to Unbootable or Pending. |
| async fn update_with_custom_config_status( |
| current_config: paver::Configuration, |
| active_config: paver::Configuration, |
| config_status_hook: Option< |
| Box<dyn Fn(&PaverEvent) -> fidl_fuchsia_paver::ConfigurationStatus + Send + Sync>, |
| >, |
| ) -> Vec<SystemUpdaterInteraction> { |
| let env = TestEnv::builder() |
| .paver_service(|builder| { |
| let builder = builder.current_config(current_config).active_config(active_config); |
| |
| if let Some(hook) = config_status_hook { |
| builder.config_status_hook(hook) |
| } else { |
| builder |
| } |
| }) |
| .build(); |
| |
| env.resolver |
| .register_package("update", "upd4t3") |
| .add_file("packages.json", make_packages_json([])) |
| .add_file("zbi", "fake_zbi"); |
| |
| env.run_update().await.expect("success"); |
| |
| assert_eq!( |
| env.get_ota_metrics().await, |
| OtaMetrics { |
| initiator: metrics::OtaResultAttemptsMetricDimensionInitiator::UserInitiatedCheck |
| as u32, |
| phase: metrics::OtaResultAttemptsMetricDimensionPhase::SuccessPendingReboot as u32, |
| status_code: metrics::OtaResultAttemptsMetricDimensionStatusCode::Success as u32, |
| target: "".into(), |
| } |
| ); |
| |
| env.take_interactions() |
| } |
| |
| // Run an update with a given current and active config, but an unchanged boot_manager config status configuration. |
| async fn update_with_current_and_active_configurations( |
| current_config: paver::Configuration, |
| active_config: paver::Configuration, |
| ) -> Vec<SystemUpdaterInteraction> { |
| update_with_custom_config_status(current_config, active_config, None).await |
| } |
| |
| // When the current partition is also the active partition, we should see a "normal" update flow. |
| async fn assert_writes_for_active_equal_to_current( |
| current_config: paver::Configuration, |
| active_config: paver::Configuration, |
| target_config: paver::Configuration, |
| ) { |
| assert_eq!( |
| update_with_current_and_active_configurations(current_config, active_config).await, |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::Kernel |
| }), |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryActiveConfiguration), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: target_config, |
| asset: paver::Asset::Kernel, |
| payload: b"fake_zbi".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: target_config }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| // When the current partition is not the active partition and current is healthy, |
| // system-updater should reset active to current, and then set active to healthy. |
| async fn assert_resets_active_when_active_not_equal_to_current( |
| current_config: paver::Configuration, |
| active_config: paver::Configuration, |
| target_config: paver::Configuration, |
| ) { |
| let interactions = |
| update_with_current_and_active_configurations(current_config, active_config).await; |
| |
| assert_eq!( |
| interactions, |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::Kernel |
| }), |
| // Get the current and active partitions, check if they match. |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryActiveConfiguration), |
| // They don't match, so get the status of current, set current to active. |
| Paver(PaverEvent::QueryConfigurationStatus { configuration: current_config }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: current_config }), |
| // Depending on the original health status of the current partition, set current |
| // healthy. |
| Paver(PaverEvent::SetConfigurationHealthy { configuration: current_config }), |
| // Set the old active unbootable and flush |
| Paver(PaverEvent::SetConfigurationUnbootable { configuration: active_config }), |
| Paver(PaverEvent::BootManagerFlush), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: target_config, |
| asset: paver::Asset::Kernel, |
| payload: b"fake_zbi".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: target_config }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |
| |
| // When current is not equal to active but current is not healthy, system-updater should not set |
| // current to healthy. |
| async fn assert_resets_active_with_unhealthy_current_partition( |
| current_config: paver::Configuration, |
| active_config: paver::Configuration, |
| target_config: paver::Configuration, |
| ) { |
| // Set the health of the current partition. |
| let current_config_clone = current_config.clone(); |
| let interactions = update_with_custom_config_status( |
| current_config, |
| active_config, |
| Some(Box::new(move |event| { |
| if let PaverEvent::QueryConfigurationStatus { configuration } = event { |
| if *configuration == current_config_clone { |
| // The current config is unbootable, all others should be fine. |
| return fidl_fuchsia_paver::ConfigurationStatus::Unbootable; |
| } |
| } |
| fidl_fuchsia_paver::ConfigurationStatus::Healthy |
| })), |
| ) |
| .await; |
| |
| assert_eq!( |
| interactions, |
| vec![ |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::VerifiedBootMetadata |
| }), |
| Paver(PaverEvent::ReadAsset { |
| configuration: current_config, |
| asset: paver::Asset::Kernel |
| }), |
| // Get the current and active partitions, check if they match. |
| Paver(PaverEvent::QueryCurrentConfiguration), |
| Paver(PaverEvent::QueryActiveConfiguration), |
| // They don't match, so get the status of current, set current to active. |
| Paver(PaverEvent::QueryConfigurationStatus { configuration: current_config }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: current_config }), |
| // The current partition was not originally healthy, so we shouldn't set it healthy now. |
| // If it was healthy, we'd expect to see `Paver(PaverEvent::SetConfigurationHealthy { |
| // configuration: current_config })`, |
| |
| // Set the old active unbootable and flush |
| Paver(PaverEvent::SetConfigurationUnbootable { configuration: active_config }), |
| Paver(PaverEvent::BootManagerFlush), |
| Gc, |
| PackageResolve(UPDATE_PKG_URL.to_string()), |
| Gc, |
| BlobfsSync, |
| Paver(PaverEvent::WriteAsset { |
| configuration: target_config, |
| asset: paver::Asset::Kernel, |
| payload: b"fake_zbi".to_vec(), |
| }), |
| Paver(PaverEvent::SetConfigurationActive { configuration: target_config }), |
| Paver(PaverEvent::DataSinkFlush), |
| Paver(PaverEvent::BootManagerFlush), |
| Reboot, |
| ] |
| ); |
| } |