Commit 04ac5bcc authored by Roman Aleksandrov's avatar Roman Aleksandrov Committed by Commit Bot

VersionUpdater: Add time left estimation.

New time left estimation estimatites time user needs to wait for end of
update process.

Bug: 1111814
Change-Id: Ie2f10c554d5695c25e160b12bbeaea15efca8b3c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2332443
Commit-Queue: Roman Aleksandrov <raleksandrov@google.com>
Reviewed-by: default avatarDenis Kuznetsov [CET] <antrim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798371}
parent 2ea849e2
...@@ -1818,6 +1818,8 @@ source_set("chromeos") { ...@@ -1818,6 +1818,8 @@ source_set("chromeos") {
"login/users/user_manager_interface.h", "login/users/user_manager_interface.h",
"login/version_info_updater.cc", "login/version_info_updater.cc",
"login/version_info_updater.h", "login/version_info_updater.h",
"login/version_updater/update_time_estimator.cc",
"login/version_updater/update_time_estimator.h",
"login/version_updater/version_updater.cc", "login/version_updater/version_updater.cc",
"login/version_updater/version_updater.h", "login/version_updater/version_updater.h",
"login/wizard_context.cc", "login/wizard_context.cc",
...@@ -3298,6 +3300,7 @@ source_set("unit_tests") { ...@@ -3298,6 +3300,7 @@ source_set("unit_tests") {
"login/users/affiliation_unittest.cc", "login/users/affiliation_unittest.cc",
"login/users/multi_profile_user_controller_unittest.cc", "login/users/multi_profile_user_controller_unittest.cc",
"login/users/user_manager_unittest.cc", "login/users/user_manager_unittest.cc",
"login/version_updater/update_time_estimator_unittest.cc",
"login/version_updater/version_updater_unittest.cc", "login/version_updater/version_updater_unittest.cc",
"mobile/mobile_activator_unittest.cc", "mobile/mobile_activator_unittest.cc",
"net/client_cert_store_chromeos_unittest.cc", "net/client_cert_store_chromeos_unittest.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/login/version_updater/update_time_estimator.h"
#include <utility>
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
namespace chromeos {
namespace {
// Estimation time needed for each stage to complete in seconds.
constexpr base::TimeDelta kDownloadTime = base::TimeDelta::FromMinutes(50);
constexpr base::TimeDelta kVerifyingTime = base::TimeDelta::FromMinutes(5);
constexpr base::TimeDelta kFinalizingTime = base::TimeDelta::FromMinutes(5);
struct StageTimeExpectation {
update_engine::Operation stage_;
base::TimeDelta time_estimation_;
};
// Stages for which |estimated_time_left| should be calculated.
constexpr StageTimeExpectation kStages[] = {
{update_engine::Operation::DOWNLOADING, kDownloadTime},
{update_engine::Operation::VERIFYING, kVerifyingTime},
{update_engine::Operation::FINALIZING, kFinalizingTime},
};
// Minimum timestep between two consecutive measurements for the download rates.
constexpr const base::TimeDelta kMinTimeStep = base::TimeDelta::FromSeconds(1);
// Smooth factor that is used for the average downloading speed
// estimation.
// avg_speed = smooth_factor * cur_speed + (1.0 - smooth_factor) *
// avg_speed.
const double kDownloadSpeedSmoothFactor = 0.1;
// Minimum allowed value for the average downloading speed.
const double kDownloadAverageSpeedDropBound = 1e-8;
// An upper bound for possible downloading time left estimations.
constexpr const base::TimeDelta kMaxTimeLeft = base::TimeDelta::FromDays(1);
} // anonymous namespace
UpdateTimeEstimator::UpdateTimeEstimator()
: tick_clock_(base::DefaultTickClock::GetInstance()) {}
void UpdateTimeEstimator::Update(const update_engine::StatusResult& status) {
if (status.current_operation() == update_engine::Operation::DOWNLOADING) {
UpdateForDownloadingTimeLeftEstimation(status);
}
UpdateForTotalTimeLeftEstimation(status.current_operation());
}
bool UpdateTimeEstimator::HasDownloadTime() const {
return has_download_time_estimation_;
}
bool UpdateTimeEstimator::HasTotalTime(update_engine::Operation stage) const {
for (const auto& el : kStages) {
if (el.stage_ == stage) {
return true;
}
}
return false;
}
base::TimeDelta UpdateTimeEstimator::GetDownloadTimeLeft() const {
return download_time_left_;
}
base::TimeDelta UpdateTimeEstimator::GetTimeLeft() const {
base::TimeDelta time_left_estimation;
bool stage_found = false;
for (const auto& el : kStages) {
if (stage_found) {
time_left_estimation += el.time_estimation_;
}
if (el.stage_ != current_stage_)
continue;
stage_found = true;
// Time spent and left in the current stage.
const base::TimeDelta time_spent =
tick_clock_->NowTicks() - stage_started_time_;
const base::TimeDelta time_left =
std::max(el.time_estimation_ - time_spent, base::TimeDelta());
if (current_stage_ == update_engine::Operation::DOWNLOADING &&
has_download_time_estimation_) {
const base::TimeDelta time_left_speed_estimation =
download_time_left_ - (tick_clock_->NowTicks() - download_last_time_);
time_left_estimation += time_left_speed_estimation;
} else {
time_left_estimation += time_left;
}
}
return time_left_estimation;
}
void UpdateTimeEstimator::UpdateForTotalTimeLeftEstimation(
update_engine::Operation stage) {
if (current_stage_ != stage) {
current_stage_ = stage;
stage_started_time_ = tick_clock_->NowTicks();
}
}
void UpdateTimeEstimator::UpdateForDownloadingTimeLeftEstimation(
const update_engine::StatusResult& status) {
if (!is_downloading_update_) {
is_downloading_update_ = true;
download_start_time_ = download_last_time_ = tick_clock_->NowTicks();
download_start_progress_ = status.progress();
download_last_progress_ = status.progress();
is_download_average_speed_computed_ = false;
download_average_speed_ = 0.0;
}
base::TimeTicks download_current_time = tick_clock_->NowTicks();
if (download_current_time < download_last_time_ + kMinTimeStep)
return;
// Estimate downloading rate.
double progress_delta =
std::max(status.progress() - download_last_progress_, 0.0);
double time_delta =
(download_current_time - download_last_time_).InSecondsF();
double download_rate = status.new_size() * progress_delta / time_delta;
download_last_time_ = download_current_time;
download_last_progress_ = status.progress();
// Estimate time left.
double progress_left = std::max(1.0 - status.progress(), 0.0);
if (!is_download_average_speed_computed_) {
download_average_speed_ = download_rate;
is_download_average_speed_computed_ = true;
}
download_average_speed_ =
kDownloadSpeedSmoothFactor * download_rate +
(1.0 - kDownloadSpeedSmoothFactor) * download_average_speed_;
if (download_average_speed_ < kDownloadAverageSpeedDropBound) {
time_delta = (download_current_time - download_start_time_).InSecondsF();
download_average_speed_ = status.new_size() * progress_delta / time_delta;
}
double work_left = progress_left * status.new_size();
// time_left is in seconds.
double time_left = work_left / download_average_speed_;
// If |download_average_speed_| is 0.
if (isnan(time_left)) {
has_download_time_estimation_ = false;
return;
}
// |time_left| may be large enough or even +infinity. So we must
// |bound possible estimations.
time_left = std::min(time_left, kMaxTimeLeft.InSecondsF());
has_download_time_estimation_ = true;
download_time_left_ =
base::TimeDelta::FromSeconds(static_cast<int>(round(time_left)));
}
} // namespace chromeos
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_VERSION_UPDATER_UPDATE_TIME_ESTIMATOR_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_VERSION_UPDATER_UPDATE_TIME_ESTIMATOR_H_
#include "base/time/time.h"
#include "chromeos/dbus/update_engine_client.h"
namespace base {
class TickClock;
}
namespace chromeos {
// Helper class that gives time left expectations.
class UpdateTimeEstimator {
public:
UpdateTimeEstimator();
// Updates data needed for estimation.
void Update(const update_engine::StatusResult& status);
bool HasDownloadTime() const;
bool HasTotalTime(update_engine::Operation stage) const;
// Estimate time left for a downloading stage to complete.
base::TimeDelta GetDownloadTimeLeft() const;
// Estimate time left for an update to complete.
base::TimeDelta GetTimeLeft() const;
void set_tick_clock_for_testing(const base::TickClock* tick_clock) {
tick_clock_ = tick_clock;
}
private:
// Starts timer that refreshes time left estimation for non-error stages.
void UpdateForTotalTimeLeftEstimation(update_engine::Operation stage);
// Updates downloading stats (remaining time and downloading
// progress), which are stored in update_info_.
void UpdateForDownloadingTimeLeftEstimation(
const update_engine::StatusResult& status);
bool has_download_time_estimation_ = false;
base::TimeDelta download_time_left_;
// Time of the first notification from the downloading stage.
base::TimeTicks download_start_time_;
double download_start_progress_ = 0;
// Time of the last notification from the downloading stage.
base::TimeTicks download_last_time_;
double download_last_progress_ = 0;
bool is_download_average_speed_computed_ = false;
// Average speed in bytes per second.
double download_average_speed_ = 0;
// Flag that is used to detect when update download has just started.
bool is_downloading_update_ = false;
// Time when stage for which total time estimation is calculated started.
base::TimeTicks stage_started_time_;
update_engine::Operation current_stage_ = update_engine::Operation::IDLE;
const base::TickClock* tick_clock_ = nullptr;
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_LOGIN_VERSION_UPDATER_UPDATE_TIME_ESTIMATOR_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/login/version_updater/update_time_estimator.h"
#include "base/test/simple_test_tick_clock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr int kFinalizingTimeInSeconds = 5 * 60;
constexpr base::TimeDelta kTimeAdvanceSeconds10 =
base::TimeDelta::FromSeconds(10);
constexpr base::TimeDelta kZeroTime = base::TimeDelta();
} // anonymous namespace
namespace chromeos {
class UpdateTimeEstimatorUnitTest : public testing::Test {
public:
UpdateTimeEstimatorUnitTest() = default;
void SetUp() override {
time_estimator_.set_tick_clock_for_testing(&tick_clock_);
}
update_engine::StatusResult CreateStatusResult(update_engine::Operation stage,
double image_size,
double progress) {
update_engine::StatusResult status;
status.set_current_operation(stage);
status.set_new_size(image_size);
status.set_progress(progress);
return status;
}
UpdateTimeEstimator time_estimator_;
base::SimpleTestTickClock tick_clock_;
private:
DISALLOW_COPY_AND_ASSIGN(UpdateTimeEstimatorUnitTest);
};
TEST_F(UpdateTimeEstimatorUnitTest, DownloadingTimeLeft) {
update_engine::StatusResult status =
CreateStatusResult(update_engine::Operation::DOWNLOADING, 1.0, 0.0);
time_estimator_.Update(status);
tick_clock_.Advance(kTimeAdvanceSeconds10);
status.set_progress(0.01);
time_estimator_.Update(status);
EXPECT_EQ(time_estimator_.GetDownloadTimeLeft().InSeconds(), 990);
tick_clock_.Advance(kTimeAdvanceSeconds10);
status.set_progress(0.10);
time_estimator_.Update(status);
EXPECT_EQ(time_estimator_.GetDownloadTimeLeft().InSeconds(), 500);
}
TEST_F(UpdateTimeEstimatorUnitTest, TotalTimeLeft) {
update_engine::StatusResult status =
CreateStatusResult(update_engine::Operation::FINALIZING, 0.0, 0.0);
time_estimator_.Update(status);
tick_clock_.Advance(kTimeAdvanceSeconds10);
EXPECT_EQ(time_estimator_.GetTimeLeft(),
base::TimeDelta::FromSeconds(kFinalizingTimeInSeconds) -
kTimeAdvanceSeconds10);
tick_clock_.Advance(base::TimeDelta::FromSeconds(kFinalizingTimeInSeconds));
EXPECT_EQ(time_estimator_.GetTimeLeft(), kZeroTime);
}
} // namespace chromeos
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "chrome/browser/chromeos/login/version_updater/version_updater.h" #include "chrome/browser/chromeos/login/version_updater/version_updater.h"
#include <algorithm> #include <algorithm>
#include <memory>
#include <string> #include <string>
#include <utility> #include <utility>
...@@ -12,6 +13,8 @@ ...@@ -12,6 +13,8 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_tick_clock.h" #include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/login/version_updater/update_time_estimator.h"
#include "chrome/grit/chromium_strings.h" #include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dbus_thread_manager.h"
...@@ -38,23 +41,11 @@ const int kBeforeVerifyingProgress = 74; ...@@ -38,23 +41,11 @@ const int kBeforeVerifyingProgress = 74;
const int kBeforeFinalizingProgress = 81; const int kBeforeFinalizingProgress = 81;
const int kProgressComplete = 100; const int kProgressComplete = 100;
// Minimum timestep between two consecutive measurements for the download rates.
constexpr const base::TimeDelta kMinTimeStep = base::TimeDelta::FromSeconds(1);
// Defines what part of update progress does download part takes. // Defines what part of update progress does download part takes.
const int kDownloadProgressIncrement = 60; const int kDownloadProgressIncrement = 60;
// Smooth factor that is used for the average downloading speed // Period of time between planned updates.
// estimation. constexpr const base::TimeDelta kUpdateTime = base::TimeDelta::FromSeconds(1);
// avg_speed = smooth_factor * cur_speed + (1.0 - smooth_factor) *
// avg_speed.
const double kDownloadSpeedSmoothFactor = 0.1;
// Minimum allowed value for the average downloading speed.
const double kDownloadAverageSpeedDropBound = 1e-8;
// An upper bound for possible downloading time left estimations.
constexpr const base::TimeDelta kMaxTimeLeft = base::TimeDelta::FromDays(1);
} // anonymous namespace } // anonymous namespace
...@@ -73,12 +64,7 @@ VersionUpdater::~VersionUpdater() { ...@@ -73,12 +64,7 @@ VersionUpdater::~VersionUpdater() {
} }
void VersionUpdater::Init() { void VersionUpdater::Init() {
download_start_progress_ = 0; time_estimator_ = UpdateTimeEstimator();
download_last_progress_ = 0;
is_download_average_speed_computed_ = false;
download_average_speed_ = 0;
is_downloading_update_ = false;
ignore_idle_status_ = true;
is_first_detection_notification_ = true; is_first_detection_notification_ = true;
update_info_ = UpdateInfo(); update_info_ = UpdateInfo();
} }
...@@ -197,6 +183,8 @@ void VersionUpdater::UpdateStatusChanged( ...@@ -197,6 +183,8 @@ void VersionUpdater::UpdateStatusChanged(
ignore_idle_status_ = false; ignore_idle_status_ = false;
} }
time_estimator_.Update(status);
bool exit_update = false; bool exit_update = false;
switch (status.current_operation()) { switch (status.current_operation()) {
case update_engine::Operation::CHECKING_FOR_UPDATE: case update_engine::Operation::CHECKING_FOR_UPDATE:
...@@ -209,19 +197,15 @@ void VersionUpdater::UpdateStatusChanged( ...@@ -209,19 +197,15 @@ void VersionUpdater::UpdateStatusChanged(
update_info_.progress_unavailable = false; update_info_.progress_unavailable = false;
break; break;
case update_engine::Operation::DOWNLOADING: case update_engine::Operation::DOWNLOADING:
if (!is_downloading_update_) { update_info_.progress_message =
is_downloading_update_ = true; l10n_util::GetStringUTF16(IDS_INSTALLING_UPDATE);
update_info_.progress_unavailable = false;
download_start_time_ = download_last_time_ = tick_clock_->NowTicks(); update_info_.progress =
download_start_progress_ = status.progress(); kBeforeDownloadProgress +
download_last_progress_ = status.progress(); static_cast<int>(status.progress() * kDownloadProgressIncrement);
is_download_average_speed_computed_ = false; update_info_.show_estimated_time_left = time_estimator_.HasDownloadTime();
download_average_speed_ = 0.0; update_info_.estimated_time_left_in_secs =
update_info_.progress_message = time_estimator_.GetDownloadTimeLeft().InSeconds();
l10n_util::GetStringUTF16(IDS_INSTALLING_UPDATE);
update_info_.progress_unavailable = false;
}
UpdateDownloadingStats(status);
break; break;
case update_engine::Operation::VERIFYING: case update_engine::Operation::VERIFYING:
update_info_.progress = kBeforeVerifyingProgress; update_info_.progress = kBeforeVerifyingProgress;
...@@ -268,54 +252,28 @@ void VersionUpdater::UpdateStatusChanged( ...@@ -268,54 +252,28 @@ void VersionUpdater::UpdateStatusChanged(
NOTREACHED(); NOTREACHED();
} }
if (time_estimator_.HasTotalTime(status.current_operation())) {
update_info_.total_time_left = time_estimator_.GetTimeLeft();
if (!refresh_timer_) {
refresh_timer_ = std::make_unique<base::RepeatingTimer>(tick_clock_);
refresh_timer_->Start(FROM_HERE, kUpdateTime, this,
&VersionUpdater::RefreshTimeLeftEstimation);
}
} else {
if (refresh_timer_) {
refresh_timer_->Stop();
refresh_timer_.reset();
}
}
delegate_->UpdateInfoChanged(update_info_); delegate_->UpdateInfoChanged(update_info_);
if (exit_update) if (exit_update)
StartExitUpdate(Result::UPDATE_NOT_REQUIRED); StartExitUpdate(Result::UPDATE_NOT_REQUIRED);
} }
void VersionUpdater::UpdateDownloadingStats( void VersionUpdater::RefreshTimeLeftEstimation() {
const update_engine::StatusResult& status) { update_info_.total_time_left = time_estimator_.GetTimeLeft();
base::TimeTicks download_current_time = tick_clock_->NowTicks(); delegate_->UpdateInfoChanged(update_info_);
if (download_current_time >= download_last_time_ + kMinTimeStep) {
// Estimate downloading rate.
double progress_delta =
std::max(status.progress() - download_last_progress_, 0.0);
double time_delta =
(download_current_time - download_last_time_).InSecondsF();
double download_rate = status.new_size() * progress_delta / time_delta;
download_last_time_ = download_current_time;
download_last_progress_ = status.progress();
// Estimate time left.
double progress_left = std::max(1.0 - status.progress(), 0.0);
if (!is_download_average_speed_computed_) {
download_average_speed_ = download_rate;
is_download_average_speed_computed_ = true;
}
download_average_speed_ =
kDownloadSpeedSmoothFactor * download_rate +
(1.0 - kDownloadSpeedSmoothFactor) * download_average_speed_;
if (download_average_speed_ < kDownloadAverageSpeedDropBound) {
time_delta = (download_current_time - download_start_time_).InSecondsF();
download_average_speed_ = status.new_size() *
(status.progress() - download_start_progress_) /
time_delta;
}
double work_left = progress_left * status.new_size();
// time_left is in seconds.
double time_left = work_left / download_average_speed_;
// |time_left| may be large enough or even +infinity. So we must
// |bound possible estimations.
time_left = std::min(time_left, kMaxTimeLeft.InSecondsF());
update_info_.show_estimated_time_left = true;
update_info_.estimated_time_left_in_secs = static_cast<int>(time_left);
}
int download_progress =
static_cast<int>(status.progress() * kDownloadProgressIncrement);
update_info_.progress = kBeforeDownloadProgress + download_progress;
} }
void VersionUpdater::OnPortalDetectionCompleted( void VersionUpdater::OnPortalDetectionCompleted(
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_VERSION_UPDATER_VERSION_UPDATER_H_ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_VERSION_UPDATER_VERSION_UPDATER_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_VERSION_UPDATER_VERSION_UPDATER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_VERSION_UPDATER_VERSION_UPDATER_H_
#include <memory>
#include <string> #include <string>
#include "base/macros.h" #include "base/macros.h"
...@@ -12,6 +13,7 @@ ...@@ -12,6 +13,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "chrome/browser/chromeos/login/screens/network_error.h" #include "chrome/browser/chromeos/login/screens/network_error.h"
#include "chrome/browser/chromeos/login/version_updater/update_time_estimator.h"
#include "chromeos/dbus/update_engine_client.h" #include "chromeos/dbus/update_engine_client.h"
#include "chromeos/network/portal_detector/network_portal_detector.h" #include "chromeos/network/portal_detector/network_portal_detector.h"
...@@ -48,7 +50,11 @@ class VersionUpdater : public UpdateEngineClient::Observer, ...@@ -48,7 +50,11 @@ class VersionUpdater : public UpdateEngineClient::Observer,
update_engine::StatusResult status; update_engine::StatusResult status;
// Estimated time left, in seconds. // Time left for an update to finish in seconds.
base::TimeDelta total_time_left;
// Estimated time left for only downloading stage, in seconds.
// TODO(crbug.com/1101317): Remove when better update is launched.
int estimated_time_left_in_secs = 0; int estimated_time_left_in_secs = 0;
bool show_estimated_time_left = false; bool show_estimated_time_left = false;
...@@ -110,6 +116,8 @@ class VersionUpdater : public UpdateEngineClient::Observer, ...@@ -110,6 +116,8 @@ class VersionUpdater : public UpdateEngineClient::Observer,
void StartNetworkCheck(); void StartNetworkCheck();
void StartUpdateCheck(); void StartUpdateCheck();
void RefreshTimeLeftEstimation();
void SetUpdateOverCellularOneTimePermission(); void SetUpdateOverCellularOneTimePermission();
void RejectUpdateOverCellular(); void RejectUpdateOverCellular();
void RebootAfterUpdate(); void RebootAfterUpdate();
...@@ -122,6 +130,7 @@ class VersionUpdater : public UpdateEngineClient::Observer, ...@@ -122,6 +130,7 @@ class VersionUpdater : public UpdateEngineClient::Observer,
void set_tick_clock_for_testing(const base::TickClock* tick_clock) { void set_tick_clock_for_testing(const base::TickClock* tick_clock) {
tick_clock_ = tick_clock; tick_clock_ = tick_clock;
time_estimator_.set_tick_clock_for_testing(tick_clock);
} }
void set_wait_for_reboot_time_for_testing( void set_wait_for_reboot_time_for_testing(
...@@ -140,10 +149,6 @@ class VersionUpdater : public UpdateEngineClient::Observer, ...@@ -140,10 +149,6 @@ class VersionUpdater : public UpdateEngineClient::Observer,
// UpdateEngineClient::Observer implementation: // UpdateEngineClient::Observer implementation:
void UpdateStatusChanged(const update_engine::StatusResult& status) override; void UpdateStatusChanged(const update_engine::StatusResult& status) override;
// Updates downloading stats (remaining time and downloading
// progress), which are stored in update_info_.
void UpdateDownloadingStats(const update_engine::StatusResult& status);
// NetworkPortalDetector::Observer implementation: // NetworkPortalDetector::Observer implementation:
void OnPortalDetectionCompleted( void OnPortalDetectionCompleted(
const NetworkState* network, const NetworkState* network,
...@@ -168,21 +173,7 @@ class VersionUpdater : public UpdateEngineClient::Observer, ...@@ -168,21 +173,7 @@ class VersionUpdater : public UpdateEngineClient::Observer,
// Pointer to delegate that owns this VersionUpdater instance. // Pointer to delegate that owns this VersionUpdater instance.
Delegate* delegate_; Delegate* delegate_;
// Time of the first notification from the downloading stage. std::unique_ptr<base::RepeatingTimer> refresh_timer_;
base::TimeTicks download_start_time_;
double download_start_progress_ = 0;
// Time of the last notification from the downloading stage.
base::TimeTicks download_last_time_;
double download_last_progress_ = 0;
bool is_download_average_speed_computed_ = false;
double download_average_speed_ = 0;
// Flag that is used to detect when update download has just started.
bool is_downloading_update_ = false;
// Ignore fist IDLE status that is sent before VersionUpdater initiated check.
bool ignore_idle_status_ = true;
// Timer for the interval to wait for the reboot. // Timer for the interval to wait for the reboot.
// If reboot didn't happen - ask user to reboot manually. // If reboot didn't happen - ask user to reboot manually.
...@@ -196,11 +187,16 @@ class VersionUpdater : public UpdateEngineClient::Observer, ...@@ -196,11 +187,16 @@ class VersionUpdater : public UpdateEngineClient::Observer,
// about state for the default network. // about state for the default network.
bool is_first_detection_notification_ = true; bool is_first_detection_notification_ = true;
// Ignore fist IDLE status that is sent before VersionUpdater initiated check.
bool ignore_idle_status_ = true;
// Stores information about current downloading process, update progress and // Stores information about current downloading process, update progress and
// state. It is sent to Delegate on each UpdateInfoChanged call, and also can // state. It is sent to Delegate on each UpdateInfoChanged call, and also can
// be obtained with corresponding getter. // be obtained with corresponding getter.
UpdateInfo update_info_; UpdateInfo update_info_;
UpdateTimeEstimator time_estimator_;
const base::TickClock* tick_clock_; const base::TickClock* tick_clock_;
base::WeakPtrFactory<VersionUpdater> weak_ptr_factory_{this}; base::WeakPtrFactory<VersionUpdater> weak_ptr_factory_{this};
......
...@@ -9,8 +9,11 @@ ...@@ -9,8 +9,11 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/test/scoped_mock_time_message_loop_task_runner.h" #include "base/test/scoped_mock_time_message_loop_task_runner.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/version_updater/mock_version_updater_delegate.h" #include "chrome/browser/chromeos/login/version_updater/mock_version_updater_delegate.h"
#include "chrome/browser/chromeos/login/version_updater/update_time_estimator.h"
#include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h" #include "chrome/browser/chromeos/net/network_portal_detector_test_impl.h"
#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h" #include "chrome/browser/chromeos/settings/device_settings_service.h"
...@@ -28,12 +31,27 @@ ...@@ -28,12 +31,27 @@
using testing::_; using testing::_;
using testing::AnyNumber; using testing::AnyNumber;
using testing::Mock;
using testing::Return; using testing::Return;
namespace chromeos { namespace chromeos {
namespace { namespace {
constexpr int kDownloadTimeInSeconds = 50 * 60;
constexpr int kVerifyingTimeInSeconds = 5 * 60;
constexpr int kFinalizingTimeInSeconds = 5 * 60;
constexpr const char kNetworkGuid[] = "test_network"; constexpr const char kNetworkGuid[] = "test_network";
MATCHER_P(TimeLeftEq, time_in_seconds, "") {
return arg.total_time_left == base::TimeDelta::FromSeconds(time_in_seconds);
}
MATCHER_P2(DowloadingTimeLeftEq, can_be_used, time, "") {
return arg.show_estimated_time_left == can_be_used &&
arg.estimated_time_left_in_secs == time;
}
} // anonymous namespace } // anonymous namespace
class VersionUpdaterUnitTest : public testing::Test { class VersionUpdaterUnitTest : public testing::Test {
...@@ -120,9 +138,10 @@ class VersionUpdaterUnitTest : public testing::Test { ...@@ -120,9 +138,10 @@ class VersionUpdaterUnitTest : public testing::Test {
std::unique_ptr<NetworkPortalDetectorTestImpl> fake_network_portal_detector_; std::unique_ptr<NetworkPortalDetectorTestImpl> fake_network_portal_detector_;
FakeUpdateEngineClient* fake_update_engine_client_; FakeUpdateEngineClient* fake_update_engine_client_;
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
private: private:
// Test versions of core browser infrastructure.
content::BrowserTaskEnvironment task_environment_;
ScopedTestingLocalState local_state_; ScopedTestingLocalState local_state_;
int checks_count_ = 0; int checks_count_ = 0;
...@@ -174,6 +193,113 @@ TEST_F(VersionUpdaterUnitTest, HandlesAvailableUpdate) { ...@@ -174,6 +193,113 @@ TEST_F(VersionUpdaterUnitTest, HandlesAvailableUpdate) {
EXPECT_EQ(fake_update_engine_client_->reboot_after_update_call_count(), 1); EXPECT_EQ(fake_update_engine_client_->reboot_after_update_call_count(), 1);
} }
// Simple time left test case expectation which does not cover using download
// speed estimation.
TEST_F(VersionUpdaterUnitTest, TimeLeftExpectation) {
SetUpMockNetworkPortalDetector();
EXPECT_CALL(*mock_delegate_, PrepareForUpdateCheck()).Times(1);
version_updater_->StartNetworkCheck();
// Verify that the DUT checks for an update.
EXPECT_EQ(fake_update_engine_client_->request_update_check_call_count(), 1);
EXPECT_CALL(*mock_delegate_, UpdateInfoChanged(TimeLeftEq(0)));
SetUpdateEngineStatus(update_engine::Operation::CHECKING_FOR_UPDATE);
Mock::VerifyAndClearExpectations(&mock_delegate_);
// All time variables here and below in seconds.
int time_left = kDownloadTimeInSeconds + kVerifyingTimeInSeconds +
kFinalizingTimeInSeconds;
EXPECT_CALL(*mock_delegate_, UpdateInfoChanged(TimeLeftEq(0.0)));
SetUpdateEngineStatus(update_engine::Operation::UPDATE_AVAILABLE);
Mock::VerifyAndClearExpectations(&mock_delegate_);
// DOWNLOADING starts.
EXPECT_CALL(*mock_delegate_, UpdateInfoChanged(TimeLeftEq(time_left)));
SetUpdateEngineStatus(update_engine::Operation::DOWNLOADING);
Mock::VerifyAndClearExpectations(&mock_delegate_);
const int time_spent_on_downloading = 50;
for (int seconds = 0; seconds < time_spent_on_downloading; seconds++) {
EXPECT_CALL(*mock_delegate_,
UpdateInfoChanged(TimeLeftEq(time_left - seconds - 1)));
}
task_environment_.FastForwardBy(
base::TimeDelta::FromSeconds(time_spent_on_downloading));
Mock::VerifyAndClearExpectations(&mock_delegate_);
// VERIFYING starts.
time_left -= kDownloadTimeInSeconds;
EXPECT_CALL(*mock_delegate_, UpdateInfoChanged(TimeLeftEq(time_left)));
SetUpdateEngineStatus(update_engine::Operation::VERIFYING);
Mock::VerifyAndClearExpectations(&mock_delegate_);
// Spent more than expected:
const int over_time = 20;
const int time_spent_on_verifying = kVerifyingTimeInSeconds + over_time;
for (int seconds = 0; seconds < kVerifyingTimeInSeconds - 1; seconds++) {
EXPECT_CALL(*mock_delegate_,
UpdateInfoChanged(TimeLeftEq(time_left - seconds - 1)));
}
EXPECT_CALL(
*mock_delegate_,
UpdateInfoChanged(TimeLeftEq(time_left - kVerifyingTimeInSeconds)))
.Times(over_time + 1);
task_environment_.FastForwardBy(
base::TimeDelta::FromSeconds(time_spent_on_verifying));
Mock::VerifyAndClearExpectations(&mock_delegate_);
// FINALIZING starts.
time_left -= kVerifyingTimeInSeconds;
EXPECT_CALL(*mock_delegate_, UpdateInfoChanged(TimeLeftEq(time_left)));
SetUpdateEngineStatus(update_engine::Operation::FINALIZING);
Mock::VerifyAndClearExpectations(&mock_delegate_);
SetStatusWithChecks(update_engine::Operation::UPDATED_NEED_REBOOT);
EXPECT_EQ(fake_update_engine_client_->reboot_after_update_call_count(), 0);
version_updater_->RebootAfterUpdate();
EXPECT_EQ(fake_update_engine_client_->reboot_after_update_call_count(), 1);
}
TEST_F(VersionUpdaterUnitTest, SimpleTimeLeftExpectationDownloadinStage) {
SetUpMockNetworkPortalDetector();
EXPECT_CALL(*mock_delegate_, PrepareForUpdateCheck()).Times(1);
version_updater_->StartNetworkCheck();
// Verify that the DUT checks for an update.
EXPECT_EQ(fake_update_engine_client_->request_update_check_call_count(), 1);
EXPECT_CALL(*mock_delegate_,
UpdateInfoChanged(DowloadingTimeLeftEq(false, 0)));
SetUpdateEngineStatus(update_engine::Operation::CHECKING_FOR_UPDATE);
Mock::VerifyAndClearExpectations(&mock_delegate_);
EXPECT_CALL(*mock_delegate_,
UpdateInfoChanged(DowloadingTimeLeftEq(false, 0)));
SetUpdateEngineStatus(update_engine::Operation::UPDATE_AVAILABLE);
Mock::VerifyAndClearExpectations(&mock_delegate_);
// DOWNLOADING starts.
update_engine::StatusResult status;
status.set_current_operation(update_engine::Operation::DOWNLOADING);
status.set_progress(0.0);
status.set_new_size(1.0);
EXPECT_CALL(*mock_delegate_,
UpdateInfoChanged(DowloadingTimeLeftEq(false, 0)));
fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
EXPECT_CALL(*mock_delegate_,
UpdateInfoChanged(DowloadingTimeLeftEq(false, 0)));
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(1));
status.set_progress(0.01);
EXPECT_CALL(*mock_delegate_,
UpdateInfoChanged(DowloadingTimeLeftEq(true, 99)));
fake_update_engine_client_->NotifyObserversThatStatusChanged(status);
Mock::VerifyAndClearExpectations(&mock_delegate_);
}
TEST_F(VersionUpdaterUnitTest, HandlesCancelUpdateOnUpdateAvailable) { TEST_F(VersionUpdaterUnitTest, HandlesCancelUpdateOnUpdateAvailable) {
SetUpMockNetworkPortalDetector(); SetUpMockNetworkPortalDetector();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment