update_engine: Add metrics to duration to apply an update.
Add new metrics that record the time between an update being seen by the client
to the time when the update is actually applied.
This metric will be recorded only for Enterprise enrolled devices.
UpdateEngine.SuccessfulUpdate.TimeRestrictedDurationFromSeenToUpdateMinutes is
recorded when an update is applied while the
DeviceAutoUpdateTimeRestrictions device policy exists.
UpdateEngine.SuccessfulUpdate.DurationFromSeenToUpdateMinutes is
recorded when an update is applied without the policy.
BUG=chromium:852860
TEST=run updater manually and checked chrome://histograms
Change-Id: I4dd54fc4404ef2e66902113617841808c1f9e616
Reviewed-on: https://chromium-review.googlesource.com/1179230
Commit-Ready: May Lippert <maybelle@chromium.org>
Tested-by: May Lippert <maybelle@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Sen Jiang <senj@chromium.org>
diff --git a/metrics_reporter_android.h b/metrics_reporter_android.h
index 8a27ef6..e320c12 100644
--- a/metrics_reporter_android.h
+++ b/metrics_reporter_android.h
@@ -91,6 +91,9 @@
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success) override {}
+ void ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) override {}
+
private:
DISALLOW_COPY_AND_ASSIGN(MetricsReporterAndroid);
};
diff --git a/metrics_reporter_interface.h b/metrics_reporter_interface.h
index b677aaa..fce8bfd 100644
--- a/metrics_reporter_interface.h
+++ b/metrics_reporter_interface.h
@@ -226,6 +226,22 @@
virtual void ReportKeyVersionMetrics(int kernel_min_version,
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success) = 0;
+
+ // Helper function to report the duration between an update being seen by the
+ // client to the update being applied. Updates are not always immediately
+ // applied when seen, several enterprise policies can affect when an update
+ // would actually be downloaded and applied.
+ //
+ // This metric should only be reported for enterprise enrolled devices.
+ //
+ // The following metrics are reported from this function:
+ // If |has_time_restriction_policy| is false:
+ // |kMetricSuccessfulUpdateDurationFromSeenDays|
+ // If |has_time_restriction_policy| is true:
+ // |kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays|
+ //
+ virtual void ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) = 0;
};
} // namespace chromeos_update_engine
diff --git a/metrics_reporter_omaha.cc b/metrics_reporter_omaha.cc
index f0c6643..3ae4f4b 100644
--- a/metrics_reporter_omaha.cc
+++ b/metrics_reporter_omaha.cc
@@ -88,6 +88,10 @@
"UpdateEngine.SuccessfulUpdate.DownloadOverheadPercentage";
const char kMetricSuccessfulUpdateDownloadSourcesUsed[] =
"UpdateEngine.SuccessfulUpdate.DownloadSourcesUsed";
+const char kMetricSuccessfulUpdateDurationFromSeenDays[] =
+ "UpdateEngine.SuccessfulUpdate.DurationFromSeenDays.NoTimeRestriction";
+const char kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays[] =
+ "UpdateEngine.SuccessfulUpdate.DurationFromSeenDays.TimeRestricted";
const char kMetricSuccessfulUpdatePayloadType[] =
"UpdateEngine.SuccessfulUpdate.PayloadType";
const char kMetricSuccessfulUpdatePayloadSizeMiB[] =
@@ -617,4 +621,19 @@
metrics_lib_->SendBoolToUMA(metric, bool_value);
}
+void MetricsReporterOmaha::ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) {
+ string metric =
+ has_time_restriction_policy
+ ? metrics::kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays
+ : metrics::kMetricSuccessfulUpdateDurationFromSeenDays;
+ LOG(INFO) << "Sending " << time_to_update_days << " for metric " << metric;
+
+ metrics_lib_->SendToUMA(metric,
+ time_to_update_days,
+ 1, // min: 1 days
+ 6 * 30, // max: 6 months (approx)
+ 50); // num_buckets
+}
+
} // namespace chromeos_update_engine
diff --git a/metrics_reporter_omaha.h b/metrics_reporter_omaha.h
index 10aef86..5680dec 100644
--- a/metrics_reporter_omaha.h
+++ b/metrics_reporter_omaha.h
@@ -69,6 +69,8 @@
extern const char kMetricSuccessfulUpdateBytesDownloadedMiB[];
extern const char kMetricSuccessfulUpdateDownloadOverheadPercentage[];
extern const char kMetricSuccessfulUpdateDownloadSourcesUsed[];
+extern const char kMetricSuccessfulUpdateDurationFromSeenDays[];
+extern const char kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays[];
extern const char kMetricSuccessfulUpdatePayloadType[];
extern const char kMetricSuccessfulUpdatePayloadSizeMiB[];
extern const char kMetricSuccessfulUpdateRebootCount[];
@@ -166,6 +168,9 @@
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success) override;
+ void ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) override;
+
private:
friend class MetricsReporterOmahaTest;
diff --git a/metrics_reporter_omaha_unittest.cc b/metrics_reporter_omaha_unittest.cc
index 878a323..a479028 100644
--- a/metrics_reporter_omaha_unittest.cc
+++ b/metrics_reporter_omaha_unittest.cc
@@ -500,4 +500,43 @@
kernel_max_rollforward_success);
}
+TEST_F(MetricsReporterOmahaTest, ReportEnterpriseUpdateSeenToDownloadDays) {
+ constexpr int kDaysToUpdate = 10;
+ constexpr int kMinBucket = 1;
+ constexpr int kMaxBucket = 6 * 30; // approximately 6 months
+ constexpr int kNumBuckets = 50;
+
+ EXPECT_CALL(*mock_metrics_lib_,
+ SendToUMA(metrics::kMetricSuccessfulUpdateDurationFromSeenDays,
+ kDaysToUpdate,
+ kMinBucket,
+ kMaxBucket,
+ kNumBuckets))
+ .Times(1);
+
+ reporter_.ReportEnterpriseUpdateSeenToDownloadDays(
+ false /* has_time_restriction_policy */, kDaysToUpdate);
+}
+
+TEST_F(MetricsReporterOmahaTest,
+ ReportEnterpriseTimeRestrictedUpdateSeenToDownloadTime) {
+ const int kDaysToUpdate = 15;
+ constexpr int kMinBucket = 1;
+ constexpr int kMaxBucket = 6 * 30; // approximately 6 months
+ constexpr int kNumBuckets = 50;
+
+ EXPECT_CALL(
+ *mock_metrics_lib_,
+ SendToUMA(
+ metrics::kMetricSuccessfulUpdateDurationFromSeenTimeRestrictedDays,
+ kDaysToUpdate,
+ kMinBucket,
+ kMaxBucket,
+ kNumBuckets))
+ .Times(1);
+
+ reporter_.ReportEnterpriseUpdateSeenToDownloadDays(
+ true /* has_time_restriction_policy */, kDaysToUpdate);
+}
+
} // namespace chromeos_update_engine
diff --git a/metrics_reporter_stub.h b/metrics_reporter_stub.h
index 486dc2f..cdb9754 100644
--- a/metrics_reporter_stub.h
+++ b/metrics_reporter_stub.h
@@ -89,6 +89,9 @@
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success) override {}
+ void ReportEnterpriseUpdateSeenToDownloadDays(
+ bool has_time_restriction_policy, int time_to_update_days) override {}
+
private:
DISALLOW_COPY_AND_ASSIGN(MetricsReporterStub);
};
diff --git a/mock_metrics_reporter.h b/mock_metrics_reporter.h
index c678a80..baf3a78 100644
--- a/mock_metrics_reporter.h
+++ b/mock_metrics_reporter.h
@@ -89,6 +89,9 @@
void(int kernel_min_version,
int kernel_max_rollforward_version,
bool kernel_max_rollforward_success));
+
+ MOCK_METHOD2(ReportEnterpriseUpdateSeenToDownloadDays,
+ void(bool has_time_restriction_policy, int time_to_update_days));
};
} // namespace chromeos_update_engine
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 95dee51..4f8cdab 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -1231,6 +1231,8 @@
return;
}
+ LoadOrPersistUpdateFirstSeenAtPref();
+
// If Omaha says to disable p2p, respect that
if (output_object.disable_p2p_for_downloading) {
LOG(INFO) << "Forcibly disabling use of p2p for downloading as "
@@ -1435,47 +1437,11 @@
OmahaRequestAction::WallClockWaitResult
OmahaRequestAction::IsWallClockBasedWaitingSatisfied(
OmahaResponse* output_object) {
- Time update_first_seen_at;
- int64_t update_first_seen_at_int;
-
- if (system_state_->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
- if (system_state_->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
- &update_first_seen_at_int)) {
- // Note: This timestamp could be that of ANY update we saw in the past
- // (not necessarily this particular update we're considering to apply)
- // but never got to apply because of some reason (e.g. stop AU policy,
- // updates being pulled out from Omaha, changes in target version prefix,
- // new update being rolled out, etc.). But for the purposes of scattering
- // it doesn't matter which update the timestamp corresponds to. i.e.
- // the clock starts ticking the first time we see an update and we're
- // ready to apply when the random wait period is satisfied relative to
- // that first seen timestamp.
- update_first_seen_at = Time::FromInternalValue(update_first_seen_at_int);
- LOG(INFO) << "Using persisted value of UpdateFirstSeenAt: "
- << utils::ToString(update_first_seen_at);
- } else {
- // This seems like an unexpected error where the persisted value exists
- // but it's not readable for some reason. Just skip scattering in this
- // case to be safe.
- LOG(INFO) << "Not scattering as UpdateFirstSeenAt value cannot be read";
- return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
- }
- } else {
- update_first_seen_at = system_state_->clock()->GetWallclockTime();
- update_first_seen_at_int = update_first_seen_at.ToInternalValue();
- if (system_state_->prefs()->SetInt64(kPrefsUpdateFirstSeenAt,
- update_first_seen_at_int)) {
- LOG(INFO) << "Persisted the new value for UpdateFirstSeenAt: "
- << utils::ToString(update_first_seen_at);
- } else {
- // This seems like an unexpected error where the value cannot be
- // persisted for some reason. Just skip scattering in this
- // case to be safe.
- LOG(INFO) << "Not scattering as UpdateFirstSeenAt value "
- << utils::ToString(update_first_seen_at)
- << " cannot be persisted";
- return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
- }
+ Time update_first_seen_at = LoadOrPersistUpdateFirstSeenAtPref();
+ if (update_first_seen_at == base::Time()) {
+ LOG(INFO) << "Not scattering as UpdateFirstSeenAt value cannot be read or "
+ "persisted";
+ return kWallClockWaitDoneAndUpdateCheckWaitNotRequired;
}
TimeDelta elapsed_time =
@@ -1914,4 +1880,47 @@
min_kernel_version, max_kernel_rollforward, max_rollforward_set);
}
+base::Time OmahaRequestAction::LoadOrPersistUpdateFirstSeenAtPref() const {
+ Time update_first_seen_at;
+ int64_t update_first_seen_at_int;
+ if (system_state_->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
+ if (system_state_->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
+ &update_first_seen_at_int)) {
+ // Note: This timestamp could be that of ANY update we saw in the past
+ // (not necessarily this particular update we're considering to apply)
+ // but never got to apply because of some reason (e.g. stop AU policy,
+ // updates being pulled out from Omaha, changes in target version prefix,
+ // new update being rolled out, etc.). But for the purposes of scattering
+ // it doesn't matter which update the timestamp corresponds to. i.e.
+ // the clock starts ticking the first time we see an update and we're
+ // ready to apply when the random wait period is satisfied relative to
+ // that first seen timestamp.
+ update_first_seen_at = Time::FromInternalValue(update_first_seen_at_int);
+ LOG(INFO) << "Using persisted value of UpdateFirstSeenAt: "
+ << utils::ToString(update_first_seen_at);
+ } else {
+ // This seems like an unexpected error where the persisted value exists
+ // but it's not readable for some reason.
+ LOG(INFO) << "UpdateFirstSeenAt value cannot be read";
+ return base::Time();
+ }
+ } else {
+ update_first_seen_at = system_state_->clock()->GetWallclockTime();
+ update_first_seen_at_int = update_first_seen_at.ToInternalValue();
+ if (system_state_->prefs()->SetInt64(kPrefsUpdateFirstSeenAt,
+ update_first_seen_at_int)) {
+ LOG(INFO) << "Persisted the new value for UpdateFirstSeenAt: "
+ << utils::ToString(update_first_seen_at);
+ } else {
+ // This seems like an unexpected error where the value cannot be
+ // persisted for some reason.
+ LOG(INFO) << "UpdateFirstSeenAt value "
+ << utils::ToString(update_first_seen_at)
+ << " cannot be persisted";
+ return base::Time();
+ }
+ }
+ return update_first_seen_at;
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_action.h b/omaha_request_action.h
index 1034c3f..e02c3be 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -322,6 +322,11 @@
// enabled.
void SetMaxKernelKeyVersionForRollback() const;
+ // Reads and returns the kPrefsUpdateFirstSeenAt pref if the pref currently
+ // exists. Otherwise saves the current wallclock time to the
+ // kPrefsUpdateFirstSeenAt pref and returns it as a base::Time object.
+ base::Time LoadOrPersistUpdateFirstSeenAtPref() const;
+
// Global system context.
SystemState* system_state_;
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 1e0ad6d..5b00238 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -1164,6 +1164,8 @@
request_params_.set_update_check_count_wait_enabled(false);
request_params_.set_waiting_period(TimeDelta::FromDays(2));
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_FALSE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
-1,
false, // ping_only
@@ -1239,6 +1241,8 @@
request_params_.set_min_update_checks_needed(0);
request_params_.set_max_update_checks_allowed(0);
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_TRUE(TestUpdateCheck(
fake_update_response_.GetUpdateResponse(),
-1,
@@ -1264,6 +1268,8 @@
request_params_.set_min_update_checks_needed(1);
request_params_.set_max_update_checks_allowed(8);
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_FALSE(TestUpdateCheck(
fake_update_response_.GetUpdateResponse(),
-1,
@@ -1302,6 +1308,8 @@
request_params_.set_min_update_checks_needed(1);
request_params_.set_max_update_checks_allowed(8);
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_TRUE(fake_prefs_.SetInt64(kPrefsUpdateCheckCount, 5));
ASSERT_FALSE(TestUpdateCheck(
@@ -1344,6 +1352,8 @@
request_params_.set_waiting_period(TimeDelta::FromDays(6));
request_params_.set_update_check_count_wait_enabled(false);
+ fake_system_state_.fake_clock()->SetWallclockTime(Time::Now());
+
ASSERT_TRUE(fake_prefs_.SetInt64(kPrefsWallClockStagingWaitPeriod, 6));
// This should not prevent scattering due to staging.
fake_update_response_.max_days_to_scatter = "0";
@@ -3008,4 +3018,51 @@
EXPECT_EQ(4, response.rollback_key_version.kernel);
}
+TEST_F(OmahaRequestActionTest,
+ TestUpdateFirstSeenAtPrefPersistedIfUpdateExists) {
+ FakeClock fake_clock;
+ Time now = Time::Now();
+ fake_clock.SetWallclockTime(now);
+ fake_system_state_.set_clock(&fake_clock);
+
+ OmahaResponse response;
+ ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_TRUE(response.update_exists);
+ EXPECT_TRUE(fake_prefs_.Exists(kPrefsUpdateFirstSeenAt));
+
+ int64_t stored_first_seen_at_time;
+ EXPECT_TRUE(fake_prefs_.GetInt64(kPrefsUpdateFirstSeenAt,
+ &stored_first_seen_at_time));
+ EXPECT_EQ(now.ToInternalValue(), stored_first_seen_at_time);
+}
+
+TEST_F(OmahaRequestActionTest,
+ TestUpdateFirstSeenAtPrefNotPersistedIfUpdateFails) {
+ FakeClock fake_clock;
+ Time now = Time::Now();
+ fake_clock.SetWallclockTime(now);
+ fake_system_state_.set_clock(&fake_clock);
+
+ OmahaResponse response;
+ ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetNoUpdateResponse(),
+ -1,
+ false, // ping_only
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kNoUpdateAvailable,
+ metrics::CheckReaction::kUnset,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ EXPECT_FALSE(response.update_exists);
+ EXPECT_FALSE(fake_prefs_.Exists(kPrefsUpdateFirstSeenAt));
+}
+
} // namespace chromeos_update_engine
diff --git a/update_attempter.cc b/update_attempter.cc
index 71a4ae1..5cc92e6 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -31,6 +31,7 @@
#include <base/rand_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
#include <brillo/bind_lambda.h>
#include <brillo/data_encoding.h>
#include <brillo/errors/error_codes.h>
@@ -982,6 +983,8 @@
if (code == ErrorCode::kSuccess) {
WriteUpdateCompletedMarker();
+ ReportTimeToUpdateAppliedMetric();
+
prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0);
prefs_->SetString(kPrefsPreviousVersion,
omaha_request_params_->app_version());
@@ -1611,4 +1614,26 @@
return false;
}
+void UpdateAttempter::ReportTimeToUpdateAppliedMetric() {
+ const policy::DevicePolicy* device_policy = system_state_->device_policy();
+ if (device_policy && device_policy->IsEnterpriseEnrolled()) {
+ vector<policy::DevicePolicy::WeeklyTimeInterval> parsed_intervals;
+ bool has_time_restrictions =
+ device_policy->GetDisallowedTimeIntervals(&parsed_intervals);
+
+ int64_t update_first_seen_at_int;
+ if (system_state_->prefs()->Exists(kPrefsUpdateFirstSeenAt)) {
+ if (system_state_->prefs()->GetInt64(kPrefsUpdateFirstSeenAt,
+ &update_first_seen_at_int)) {
+ TimeDelta update_delay =
+ system_state_->clock()->GetWallclockTime() -
+ Time::FromInternalValue(update_first_seen_at_int);
+ system_state_->metrics_reporter()
+ ->ReportEnterpriseUpdateSeenToDownloadDays(has_time_restrictions,
+ update_delay.InDays());
+ }
+ }
+ }
+}
+
} // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
index 108a6c6..8fcaba5 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -413,6 +413,11 @@
void CalculateStagingParams(bool interactive);
+ // Reports a metric that tracks the time from when the update was first seen
+ // to the time when the update was finally downloaded and applied. This metric
+ // will only be reported for enterprise enrolled devices.
+ void ReportTimeToUpdateAppliedMetric();
+
// Last status notification timestamp used for throttling. Use monotonic
// TimeTicks to ensure that notifications are sent even if the system clock is
// set back in the middle of an update.
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 93fcef1..b33f70d 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -1422,4 +1422,84 @@
attempter_.ProcessingDone(nullptr, ErrorCode::kRollbackNotPossible);
}
+TEST_F(UpdateAttempterTest, TimeToUpdateAppliedMetricFailure) {
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportEnterpriseUpdateSeenToDownloadDays(_, _))
+ .Times(0);
+ attempter_.ProcessingDone(nullptr, ErrorCode::kOmahaUpdateDeferredPerPolicy);
+}
+
+TEST_F(UpdateAttempterTest, TimeToUpdateAppliedOnNonEnterprise) {
+ auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+ fake_system_state_.set_device_policy(device_policy.get());
+ // Make device policy return that this is not enterprise enrolled
+ EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(false));
+
+ // Ensure that the metric is not recorded.
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportEnterpriseUpdateSeenToDownloadDays(_, _))
+ .Times(0);
+ attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest,
+ TimeToUpdateAppliedWithTimeRestrictionMetricSuccess) {
+ constexpr int kDaysToUpdate = 15;
+ auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+ fake_system_state_.set_device_policy(device_policy.get());
+ // Make device policy return that this is enterprise enrolled
+ EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(true));
+ // Pretend that there's a time restriction policy in place
+ EXPECT_CALL(*device_policy, GetDisallowedTimeIntervals(_))
+ .WillOnce(Return(true));
+
+ FakePrefs fake_prefs;
+ Time update_first_seen_at = Time::Now();
+ fake_prefs.SetInt64(kPrefsUpdateFirstSeenAt,
+ update_first_seen_at.ToInternalValue());
+
+ FakeClock fake_clock;
+ Time update_finished_at =
+ update_first_seen_at + TimeDelta::FromDays(kDaysToUpdate);
+ fake_clock.SetWallclockTime(update_finished_at);
+
+ fake_system_state_.set_clock(&fake_clock);
+ fake_system_state_.set_prefs(&fake_prefs);
+
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportEnterpriseUpdateSeenToDownloadDays(true, kDaysToUpdate))
+ .Times(1);
+ attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
+TEST_F(UpdateAttempterTest,
+ TimeToUpdateAppliedWithoutTimeRestrictionMetricSuccess) {
+ constexpr int kDaysToUpdate = 15;
+ auto device_policy = std::make_unique<policy::MockDevicePolicy>();
+ fake_system_state_.set_device_policy(device_policy.get());
+ // Make device policy return that this is enterprise enrolled
+ EXPECT_CALL(*device_policy, IsEnterpriseEnrolled()).WillOnce(Return(true));
+ // Pretend that there's no time restriction policy in place
+ EXPECT_CALL(*device_policy, GetDisallowedTimeIntervals(_))
+ .WillOnce(Return(false));
+
+ FakePrefs fake_prefs;
+ Time update_first_seen_at = Time::Now();
+ fake_prefs.SetInt64(kPrefsUpdateFirstSeenAt,
+ update_first_seen_at.ToInternalValue());
+
+ FakeClock fake_clock;
+ Time update_finished_at =
+ update_first_seen_at + TimeDelta::FromDays(kDaysToUpdate);
+ fake_clock.SetWallclockTime(update_finished_at);
+
+ fake_system_state_.set_clock(&fake_clock);
+ fake_system_state_.set_prefs(&fake_prefs);
+
+ EXPECT_CALL(*fake_system_state_.mock_metrics_reporter(),
+ ReportEnterpriseUpdateSeenToDownloadDays(false, kDaysToUpdate))
+ .Times(1);
+ attempter_.ProcessingDone(nullptr, ErrorCode::kSuccess);
+}
+
} // namespace chromeos_update_engine