blob: 5b4a05e0d17ade84921fdc0a997db14f41eba2b7 [file] [log] [blame]
// Copyright 2019 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 crate::config::Config;
use crate::update_manager::UpdateManagerControlHandle;
use crate::update_monitor::StateNotifier;
use fidl_fuchsia_update::CheckNotStartedReason;
use fidl_fuchsia_update_ext::{CheckOptions, Initiator};
use fuchsia_async as fasync;
use fuchsia_syslog::fx_log_info;
use futures::prelude::*;
pub fn run_periodic_update_check<N>(
mut manager: UpdateManagerControlHandle<N>,
config: &Config,
) -> impl Future<Output = ()>
where
N: StateNotifier,
{
let timer = config.poll_frequency().map(|duration| fasync::Interval::new(duration.into()));
async move {
let mut timer = match timer {
Some(timer) => timer,
None => return,
};
while let Some(()) = timer.next().await {
let options = CheckOptions::builder().initiator(Initiator::Service).build();
match manager.try_start_update(options, None).await {
Ok(()) => {}
Err(CheckNotStartedReason::Throttled) => {
fx_log_info!("Service initiated update check throttled");
}
Err(CheckNotStartedReason::AlreadyInProgress) => {
fx_log_info!("Update in progress, automatic update check skipped");
}
Err(CheckNotStartedReason::Internal) => {
fx_log_info!("Internal error, will try again later");
}
Err(CheckNotStartedReason::InvalidOptions) => {
fx_log_info!("Invalid options, update check skipped");
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::ConfigBuilder;
use crate::update_manager::{
tests::{
FakeCommitQuerier, FakeCurrentChannelUpdater, FakeTargetChannelUpdater,
FakeUpdateChecker, FakeUpdateManagerControlHandle, StateChangeCollector,
UnreachableUpdateApplier,
},
UpdateManager, UpdateManagerRequest,
};
use fidl_fuchsia_update_ext::{CheckOptions, State};
use fuchsia_async::DurationExt;
use fuchsia_zircon::DurationNum;
use futures::task::Poll;
use matches::assert_matches;
use std::sync::Arc;
#[test]
fn test_disabled_periodic_update_check() {
let mut executor = fasync::Executor::new_with_fake_time().unwrap();
let (manager, mut requests) = FakeUpdateManagerControlHandle::<StateChangeCollector>::new();
let mut cron = run_periodic_update_check(manager, &Config::default()).boxed();
assert_eq!(Poll::Ready(()), executor.run_until_stalled(&mut cron));
assert_matches!(requests.next(), None);
assert_eq!(None, executor.wake_next_timer());
}
#[test]
fn test_periodic_update_check() {
let mut executor = fasync::Executor::new_with_fake_time().unwrap();
let (manager, mut requests) = FakeUpdateManagerControlHandle::<StateChangeCollector>::new();
let period = 10.minutes();
let config = ConfigBuilder::new().poll_frequency(period).build();
let mut cron = run_periodic_update_check(manager, &config).boxed();
// Let the cron task set up the timer, but nothing interesting happens until time advances.
assert_eq!(Poll::Pending, executor.run_until_stalled(&mut cron));
assert_matches!(requests.next(), None);
// Not time yet.
executor.set_fake_time(8.minutes().after_now());
assert!(!executor.wake_expired_timers());
assert_eq!(Poll::Pending, executor.run_until_stalled(&mut cron));
assert_matches!(requests.next(), None);
// Verify the timer performs the correct operations after elapsing.
executor.set_fake_time(2.minutes().after_now());
assert!(executor.wake_expired_timers());
assert_eq!(Poll::Pending, executor.run_until_stalled(&mut cron));
assert_matches!(
requests.next(),
Some(UpdateManagerRequest::TryStartUpdate {
options: CheckOptions {
initiator: Initiator::Service,
allow_attaching_to_existing_update_check: false,
},
callback: None,
responder: _,
})
);
assert_eq!(Poll::Pending, executor.run_until_stalled(&mut cron));
assert_matches!(requests.next(), None);
// Verify the timer fires more than once.
executor.set_fake_time(period.after_now());
assert!(executor.wake_expired_timers());
assert_eq!(Poll::Pending, executor.run_until_stalled(&mut cron));
assert_matches!(
requests.next(),
Some(UpdateManagerRequest::TryStartUpdate {
options: CheckOptions {
initiator: Initiator::Service,
allow_attaching_to_existing_update_check: false,
},
callback: None,
responder: _,
})
);
assert_eq!(Poll::Pending, executor.run_until_stalled(&mut cron));
assert_matches!(requests.next(), None);
}
#[test]
fn test_simultaneous_user_update_check_and_periodic_update_check() {
let mut executor = fasync::Executor::new_with_fake_time().unwrap();
let checker = FakeUpdateChecker::new_up_to_date();
let update_blocked = checker.block().unwrap();
let callback = StateChangeCollector::new();
let mut fut =
UpdateManager::<_, _, _, _, StateChangeCollector, _>::from_checker_and_applier(
Arc::new(FakeTargetChannelUpdater::new()),
Arc::new(FakeCurrentChannelUpdater::new()),
checker.clone(),
UnreachableUpdateApplier,
FakeCommitQuerier::new(),
)
.boxed();
let mut manager = match executor.run_until_stalled(&mut fut) {
Poll::Ready(manager) => manager,
Poll::Pending => panic!("manager not ready"),
}
.spawn();
let period = 24.hours();
let config = ConfigBuilder::new().poll_frequency(period).build();
let mut cron = run_periodic_update_check(manager.clone(), &config).boxed();
// Let the cron task set up the timer, but nothing interesting happens until time advances.
assert_eq!(Poll::Pending, executor.run_until_stalled(&mut cron));
// User wins, and only 1 update check happens.
let options = CheckOptions::builder().initiator(Initiator::User).build();
let mut fut = manager.try_start_update(options, Some(callback.clone())).boxed();
assert_eq!(executor.run_until_stalled(&mut fut), Poll::Ready(Ok(())));
// The automatic update is skipped because an update is already in progress.
executor.set_fake_time(period.after_now());
assert!(executor.wake_expired_timers());
assert_eq!(Poll::Pending, executor.run_until_stalled(&mut cron));
// Let the user-initiated update check complete. The update does not run.
std::mem::drop(update_blocked);
assert_eq!(Poll::Pending, executor.run_until_stalled(&mut cron));
assert_eq!(checker.call_count(), 1);
assert_eq!(
callback.take_states(),
vec![State::CheckingForUpdates, State::NoUpdateAvailable]
);
}
}