blob: 9cfcc3eb75fef3c157c89918f2d625dfd1f91e8a [file] [log] [blame]
// Copyright (c) 2012 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 "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
#include <stdint.h>
#include <memory>
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "build/build_config.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_service.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
#include "services/network/test/test_network_connection_tracker.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace em = enterprise_management;
using testing::Mock;
using testing::_;
namespace policy {
namespace {
const int64_t kPolicyRefreshRate = 4 * 60 * 60 * 1000;
const int64_t kInitialCacheAgeMinutes = 1;
} // namespace
class CloudPolicyRefreshSchedulerTest : public testing::Test {
protected:
CloudPolicyRefreshSchedulerTest()
: service_(std::make_unique<MockCloudPolicyService>(&client_, &store_)),
task_runner_(new base::TestSimpleTaskRunner()) {}
void SetUp() override {
client_.SetDMToken("token");
// Set up the protobuf timestamp to be one minute in the past. Since the
// protobuf field only has millisecond precision, we convert the actual
// value back to get a millisecond-clamped time stamp for the checks below.
store_.policy_.reset(new em::PolicyData());
base::Time now = base::Time::NowFromSystemTime();
base::TimeDelta initial_age =
base::TimeDelta::FromMinutes(kInitialCacheAgeMinutes);
store_.policy_->set_timestamp((now - initial_age).ToJavaTime());
last_update_ = base::Time::FromJavaTime(store_.policy_->timestamp());
last_update_ticks_ = base::TimeTicks::Now() +
(last_update_ - base::Time::NowFromSystemTime());
}
CloudPolicyRefreshScheduler* CreateRefreshScheduler() {
EXPECT_EQ(0u, task_runner_->NumPendingTasks());
CloudPolicyRefreshScheduler* scheduler = new CloudPolicyRefreshScheduler(
&client_, &store_, service_.get(), task_runner_,
network::TestNetworkConnectionTracker::CreateGetter());
// Make sure the NetworkConnectionTracker has been set up.
base::RunLoop().RunUntilIdle();
scheduler->SetDesiredRefreshDelay(kPolicyRefreshRate);
return scheduler;
}
void NotifyConnectionChanged() {
network::TestNetworkConnectionTracker::GetInstance()->SetConnectionType(
network::mojom::ConnectionType::CONNECTION_WIFI);
base::RunLoop().RunUntilIdle();
}
void EmulateSleepThroughLastRefreshTime(
CloudPolicyRefreshScheduler* const scheduler) {
// Simulate a sleep of the device by decreasing the wall clock based refresh
// timestamp, so that the next refresh time point, calculated from it, turns
// out to be earlier than the next refresh time point, calculated from the
// ticks count clock.
scheduler->set_last_refresh_for_testing(base::Time::NowFromSystemTime() -
base::TimeDelta::FromDays(1));
}
base::TimeDelta GetLastDelay() const {
if (!task_runner_->HasPendingTask())
return base::TimeDelta();
return task_runner_->FinalPendingTaskDelay();
}
void CheckTiming(int64_t expected_delay_ms) const {
CheckTimingWithAge(base::TimeDelta::FromMilliseconds(expected_delay_ms),
base::TimeDelta());
}
// Checks that the latest refresh scheduled used an offset of
// |offset_from_last_refresh| from the time of the previous refresh.
// |cache_age| is how old the cache was when the refresh was issued.
void CheckTimingWithAge(const base::TimeDelta& offset_from_last_refresh,
const base::TimeDelta& cache_age) const {
EXPECT_TRUE(task_runner_->HasPendingTask());
base::Time now(base::Time::NowFromSystemTime());
base::TimeTicks now_ticks(base::TimeTicks::Now());
// |last_update_| was updated and then a refresh was scheduled at time S,
// so |last_update_| is a bit before that.
// Now is a bit later, N.
// GetLastDelay() + S is the time when the refresh will run, T.
// |cache_age| is the age of the cache at time S. It was thus created at
// S - cache_age.
//
// Schematically:
//
// . S . N . . . . . . . T . . . .
// | | |
// set "last_refresh_" and then scheduled the next refresh; the cache
// was "cache_age" old at this point.
// | |
// some time elapsed on the test execution since then;
// this is the current time, "now"
// |
// the refresh will execute at this time
//
// So the exact delay is T - S - |cache_age|, but we don't have S here.
//
// |last_update_| was a bit before S, so if
// elapsed = now - |last_update_| then the delay is more than
// |offset_from_last_refresh| - elapsed.
//
// The delay is also less than offset_from_last_refresh, because some time
// already elapsed. Additionally, if the cache was already considered old
// when the schedule was performed then its age at that time has been
// discounted from the delay. So the delay is a bit less than
// |offset_from_last_refresh - cache_age|.
// The logic of time based on TimeTicks is added to be on the safe side,
// since CloudPolicyRefreshScheduler implementation is based on both, the
// system time and the time in TimeTicks.
base::TimeDelta system_delta = (now - last_update_);
base::TimeDelta ticks_delta = (now_ticks - last_update_ticks_);
EXPECT_GE(GetLastDelay(),
offset_from_last_refresh - std::max(system_delta, ticks_delta));
EXPECT_LE(GetLastDelay(), offset_from_last_refresh - cache_age);
}
void CheckInitialRefresh(bool with_invalidations) const {
#if defined(OS_ANDROID) || defined(OS_IOS)
// The mobile platforms take the cache age into account for the initial
// fetch. Usually the cache age is ignored for the initial refresh, but on
// mobile it's used to restrain from refreshing on every startup.
base::TimeDelta rate = base::TimeDelta::FromMilliseconds(
with_invalidations
? CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs
: kPolicyRefreshRate);
CheckTimingWithAge(rate,
base::TimeDelta::FromMinutes(kInitialCacheAgeMinutes));
#else
// Other platforms refresh immediately.
EXPECT_EQ(base::TimeDelta(), GetLastDelay());
#endif
}
void SetLastUpdateToNow() {
last_update_ = base::Time::NowFromSystemTime();
last_update_ticks_ = base::TimeTicks::Now();
}
base::test::ScopedTaskEnvironment task_environment_;
MockCloudPolicyClient client_;
MockCloudPolicyStore store_;
std::unique_ptr<MockCloudPolicyService> service_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
// Base time for the refresh that the scheduler should be using.
base::Time last_update_;
base::TimeTicks last_update_ticks_;
};
TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshNoPolicy) {
store_.policy_.reset();
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
CreateRefreshScheduler());
EXPECT_TRUE(task_runner_->HasPendingTask());
EXPECT_EQ(GetLastDelay(), base::TimeDelta());
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunUntilIdle();
}
TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshUnmanaged) {
store_.policy_->set_state(em::PolicyData::UNMANAGED);
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
CreateRefreshScheduler());
CheckTiming(CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs);
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunUntilIdle();
}
TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshManagedNotYetFetched) {
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
CreateRefreshScheduler());
EXPECT_TRUE(task_runner_->HasPendingTask());
CheckInitialRefresh(false);
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunUntilIdle();
}
TEST_F(CloudPolicyRefreshSchedulerTest, InitialRefreshManagedAlreadyFetched) {
SetLastUpdateToNow();
client_.SetPolicy(dm_protocol::kChromeUserPolicyType, std::string(),
em::PolicyFetchResponse());
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
CreateRefreshScheduler());
CheckTiming(kPolicyRefreshRate);
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunUntilIdle();
}
TEST_F(CloudPolicyRefreshSchedulerTest, Unregistered) {
client_.SetDMToken(std::string());
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
CreateRefreshScheduler());
client_.NotifyPolicyFetched();
client_.NotifyRegistrationStateChanged();
client_.NotifyClientError();
scheduler->SetDesiredRefreshDelay(12 * 60 * 60 * 1000);
store_.NotifyStoreLoaded();
store_.NotifyStoreError();
EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(CloudPolicyRefreshSchedulerTest, RefreshSoon) {
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
CreateRefreshScheduler());
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
scheduler->RefreshSoon();
task_runner_->RunUntilIdle();
Mock::VerifyAndClearExpectations(&client_);
}
TEST_F(CloudPolicyRefreshSchedulerTest, RefreshSoonOverriding) {
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
CreateRefreshScheduler());
// The refresh scheduled for soon overrides the previously scheduled refresh.
scheduler->RefreshSoon();
CheckTiming(0);
// The refresh scheduled for soon is not overridden by the change of the
// desired refresh delay.
const int64_t kNewPolicyRefreshRate = 12 * 60 * 60 * 1000;
scheduler->SetDesiredRefreshDelay(kNewPolicyRefreshRate);
CheckTiming(0);
// The refresh scheduled for soon is not overridden by the notification on the
// already fetched policy.
client_.SetPolicy(dm_protocol::kChromeUserPolicyType, std::string(),
em::PolicyFetchResponse());
store_.NotifyStoreLoaded();
CheckTiming(0);
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunUntilIdle();
Mock::VerifyAndClearExpectations(&client_);
// The next refresh is scheduled according to the normal rate.
client_.NotifyPolicyFetched();
CheckTiming(kNewPolicyRefreshRate);
}
TEST_F(CloudPolicyRefreshSchedulerTest, InvalidationsAvailable) {
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
new CloudPolicyRefreshScheduler(
&client_, &store_, service_.get(), task_runner_,
network::TestNetworkConnectionTracker::CreateGetter()));
scheduler->SetDesiredRefreshDelay(kPolicyRefreshRate);
// The scheduler has scheduled refreshes at the initial refresh rate.
EXPECT_EQ(2u, task_runner_->NumPendingTasks());
// Signal that invalidations are available.
scheduler->SetInvalidationServiceAvailability(true);
EXPECT_EQ(CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs,
scheduler->GetActualRefreshDelay());
EXPECT_EQ(3u, task_runner_->NumPendingTasks());
CheckInitialRefresh(true);
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunPendingTasks();
Mock::VerifyAndClearExpectations(&client_);
// Complete that fetch.
SetLastUpdateToNow();
client_.NotifyPolicyFetched();
// The next refresh has been scheduled using a lower refresh rate.
EXPECT_EQ(1u, task_runner_->NumPendingTasks());
CheckTiming(CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs);
}
TEST_F(CloudPolicyRefreshSchedulerTest, InvalidationsNotAvailable) {
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
new CloudPolicyRefreshScheduler(
&client_, &store_, service_.get(), task_runner_,
network::TestNetworkConnectionTracker::CreateGetter()));
scheduler->SetDesiredRefreshDelay(kPolicyRefreshRate);
// Signal that invalidations are not available. The scheduler will not
// schedule refreshes since the available state is not changed.
for (int i = 0; i < 10; ++i) {
scheduler->SetInvalidationServiceAvailability(false);
EXPECT_EQ(2u, task_runner_->NumPendingTasks());
}
// This scheduled the initial refresh.
CheckInitialRefresh(false);
EXPECT_EQ(kPolicyRefreshRate, scheduler->GetActualRefreshDelay());
// Perform that fetch now.
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunPendingTasks();
Mock::VerifyAndClearExpectations(&client_);
// Complete that fetch.
SetLastUpdateToNow();
client_.NotifyPolicyFetched();
// The next refresh has been scheduled at the normal rate.
EXPECT_EQ(1u, task_runner_->NumPendingTasks());
CheckTiming(kPolicyRefreshRate);
}
TEST_F(CloudPolicyRefreshSchedulerTest, InvalidationsOffAndOn) {
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
new CloudPolicyRefreshScheduler(
&client_, &store_, service_.get(), task_runner_,
network::TestNetworkConnectionTracker::CreateGetter()));
scheduler->SetDesiredRefreshDelay(kPolicyRefreshRate);
scheduler->SetInvalidationServiceAvailability(true);
// Initial fetch.
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunUntilIdle();
Mock::VerifyAndClearExpectations(&client_);
SetLastUpdateToNow();
client_.NotifyPolicyFetched();
// The next refresh has been scheduled using a lower refresh rate.
CheckTiming(CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs);
// If the service goes down and comes back up before the timeout then a
// refresh is rescheduled at the lower rate again; after executing all
// pending tasks only 1 fetch is performed.
scheduler->SetInvalidationServiceAvailability(false);
scheduler->SetInvalidationServiceAvailability(true);
// The next refresh has been scheduled using a lower refresh rate.
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
CheckTiming(CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs);
task_runner_->RunPendingTasks();
Mock::VerifyAndClearExpectations(&client_);
}
TEST_F(CloudPolicyRefreshSchedulerTest, InvalidationsDisconnected) {
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
new CloudPolicyRefreshScheduler(
&client_, &store_, service_.get(), task_runner_,
network::TestNetworkConnectionTracker::CreateGetter()));
scheduler->SetDesiredRefreshDelay(kPolicyRefreshRate);
scheduler->SetInvalidationServiceAvailability(true);
// Initial fetch.
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunUntilIdle();
Mock::VerifyAndClearExpectations(&client_);
SetLastUpdateToNow();
client_.NotifyPolicyFetched();
// The next refresh has been scheduled using a lower refresh rate.
// Flush that task.
CheckTiming(CloudPolicyRefreshScheduler::kWithInvalidationsRefreshDelayMs);
EXPECT_CALL(*service_.get(), RefreshPolicy(_)).Times(1);
EXPECT_CALL(client_, FetchPolicy()).Times(1);
task_runner_->RunPendingTasks();
Mock::VerifyAndClearExpectations(&client_);
// If the service goes down then the refresh scheduler falls back on the
// default polling rate.
scheduler->SetInvalidationServiceAvailability(false);
CheckTiming(kPolicyRefreshRate);
}
TEST_F(CloudPolicyRefreshSchedulerTest, OnConnectionChangedUnregistered) {
client_.SetDMToken(std::string());
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
CreateRefreshScheduler());
client_.NotifyClientError();
EXPECT_FALSE(task_runner_->HasPendingTask());
EmulateSleepThroughLastRefreshTime(scheduler.get());
scheduler->OnConnectionChanged(
network::mojom::ConnectionType::CONNECTION_WIFI);
EXPECT_FALSE(task_runner_->HasPendingTask());
}
// TODO(igorcov): Before sleep in normal flow there's a task pending. When the
// device wakes up, OnConnectionChanged is called which should cancel the
// pending task and queue a new task to run earlier. It is desirable to
// simulate that flow here.
TEST_F(CloudPolicyRefreshSchedulerTest, OnConnectionChangedAfterSleep) {
std::unique_ptr<CloudPolicyRefreshScheduler> scheduler(
CreateRefreshScheduler());
client_.SetPolicy(dm_protocol::kChromeUserPolicyType, std::string(),
em::PolicyFetchResponse());
task_runner_->RunPendingTasks();
EXPECT_FALSE(task_runner_->HasPendingTask());
EmulateSleepThroughLastRefreshTime(scheduler.get());
scheduler->OnConnectionChanged(
network::mojom::ConnectionType::CONNECTION_WIFI);
EXPECT_TRUE(task_runner_->HasPendingTask());
task_runner_->ClearPendingTasks();
}
class CloudPolicyRefreshSchedulerSteadyStateTest
: public CloudPolicyRefreshSchedulerTest {
protected:
CloudPolicyRefreshSchedulerSteadyStateTest() {}
void SetUp() override {
refresh_scheduler_.reset(CreateRefreshScheduler());
refresh_scheduler_->SetDesiredRefreshDelay(kPolicyRefreshRate);
CloudPolicyRefreshSchedulerTest::SetUp();
SetLastUpdateToNow();
client_.NotifyPolicyFetched();
CheckTiming(kPolicyRefreshRate);
}
std::unique_ptr<CloudPolicyRefreshScheduler> refresh_scheduler_;
};
TEST_F(CloudPolicyRefreshSchedulerSteadyStateTest, OnPolicyFetched) {
client_.NotifyPolicyFetched();
CheckTiming(kPolicyRefreshRate);
}
TEST_F(CloudPolicyRefreshSchedulerSteadyStateTest, OnRegistrationStateChanged) {
client_.SetDMToken("new_token");
client_.NotifyRegistrationStateChanged();
EXPECT_EQ(GetLastDelay(), base::TimeDelta());
task_runner_->ClearPendingTasks();
client_.SetDMToken(std::string());
client_.NotifyRegistrationStateChanged();
EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(CloudPolicyRefreshSchedulerSteadyStateTest, OnStoreLoaded) {
store_.NotifyStoreLoaded();
CheckTiming(kPolicyRefreshRate);
}
TEST_F(CloudPolicyRefreshSchedulerSteadyStateTest, OnStoreError) {
task_runner_->ClearPendingTasks();
store_.NotifyStoreError();
EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(CloudPolicyRefreshSchedulerSteadyStateTest, RefreshDelayChange) {
const int delay_short_ms = 5 * 60 * 1000;
refresh_scheduler_->SetDesiredRefreshDelay(delay_short_ms);
CheckTiming(CloudPolicyRefreshScheduler::kRefreshDelayMinMs);
const int delay_ms = 12 * 60 * 60 * 1000;
refresh_scheduler_->SetDesiredRefreshDelay(delay_ms);
CheckTiming(delay_ms);
const int delay_long_ms = 20 * 24 * 60 * 60 * 1000;
refresh_scheduler_->SetDesiredRefreshDelay(delay_long_ms);
CheckTiming(CloudPolicyRefreshScheduler::kRefreshDelayMaxMs);
}
TEST_F(CloudPolicyRefreshSchedulerSteadyStateTest, OnConnectionChanged) {
client_.SetStatus(DM_STATUS_REQUEST_FAILED);
NotifyConnectionChanged();
EXPECT_EQ(GetLastDelay(), base::TimeDelta());
}
struct ClientErrorTestParam {
DeviceManagementStatus client_error;
int64_t expected_delay_ms;
int backoff_factor;
};
static const ClientErrorTestParam kClientErrorTestCases[] = {
{DM_STATUS_REQUEST_INVALID,
CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs, 1},
{DM_STATUS_REQUEST_FAILED,
CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs, 2},
{DM_STATUS_TEMPORARY_UNAVAILABLE,
CloudPolicyRefreshScheduler::kInitialErrorRetryDelayMs, 2},
{DM_STATUS_HTTP_STATUS_ERROR,
CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs, 1},
{DM_STATUS_RESPONSE_DECODING_ERROR,
CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs, 1},
{DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED,
CloudPolicyRefreshScheduler::kUnmanagedRefreshDelayMs, 1},
{DM_STATUS_SERVICE_DEVICE_NOT_FOUND, -1, 1},
{DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID, -1, 1},
{DM_STATUS_SERVICE_ACTIVATION_PENDING, kPolicyRefreshRate, 1},
{DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER, -1, 1},
{DM_STATUS_SERVICE_MISSING_LICENSES, -1, 1},
{DM_STATUS_SERVICE_DEVICE_ID_CONFLICT, -1, 1},
{DM_STATUS_SERVICE_POLICY_NOT_FOUND, kPolicyRefreshRate, 1},
{DM_STATUS_SERVICE_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE, -1, 1},
};
class CloudPolicyRefreshSchedulerClientErrorTest
: public CloudPolicyRefreshSchedulerSteadyStateTest,
public testing::WithParamInterface<ClientErrorTestParam> {};
TEST_P(CloudPolicyRefreshSchedulerClientErrorTest, OnClientError) {
client_.SetStatus(GetParam().client_error);
task_runner_->ClearPendingTasks();
// See whether the error triggers the right refresh delay.
int64_t expected_delay_ms = GetParam().expected_delay_ms;
client_.NotifyClientError();
if (expected_delay_ms >= 0) {
CheckTiming(expected_delay_ms);
// Check whether exponential backoff is working as expected and capped at
// the regular refresh rate (if applicable).
do {
expected_delay_ms *= GetParam().backoff_factor;
SetLastUpdateToNow();
client_.NotifyClientError();
CheckTiming(std::max(std::min(expected_delay_ms, kPolicyRefreshRate),
GetParam().expected_delay_ms));
} while (GetParam().backoff_factor > 1 &&
expected_delay_ms <= kPolicyRefreshRate);
} else {
EXPECT_EQ(base::TimeDelta(), GetLastDelay());
EXPECT_FALSE(task_runner_->HasPendingTask());
}
}
INSTANTIATE_TEST_CASE_P(CloudPolicyRefreshSchedulerClientErrorTest,
CloudPolicyRefreshSchedulerClientErrorTest,
testing::ValuesIn(kClientErrorTestCases));
} // namespace policy