1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/policy/heartbeat_scheduler.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/test/test_simple_task_runner.h"
9 #include "chrome/browser/chromeos/settings/cros_settings.h"
10 #include "chrome/browser/chromeos/settings/device_settings_service.h"
11 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
12 #include "chromeos/settings/cros_settings_names.h"
13 #include "components/gcm_driver/fake_gcm_driver.h"
14 #include "content/public/test/test_utils.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
19 using ::testing::SaveArg
;
22 const char* const kFakeEnrollmentDomain
= "example.com";
23 const char* const kFakeDeviceId
= "fake_device_id";
24 const char* const kHeartbeatGCMAppID
= "com.google.chromeos.monitoring";
26 class MockGCMDriver
: public testing::StrictMock
<gcm::FakeGCMDriver
> {
31 ~MockGCMDriver() override
{
34 MOCK_METHOD2(RegisterImpl
,
35 void(const std::string
&, const std::vector
<std::string
>&));
36 MOCK_METHOD3(SendImpl
,
37 void(const std::string
&, const std::string
&,
38 const gcm::GCMClient::OutgoingMessage
& message
));
41 // Helper function to complete a registration previously started by
43 void CompleteRegistration(const std::string
& app_id
,
44 gcm::GCMClient::Result result
) {
45 RegisterFinished(app_id
, "registration_id", result
);
48 // Helper function to complete a send operation previously started by
50 void CompleteSend(const std::string
& app_id
,
51 const std::string
& message_id
,
52 gcm::GCMClient::Result result
) {
53 SendFinished(app_id
, message_id
, result
);
57 class HeartbeatSchedulerTest
: public testing::Test
{
59 HeartbeatSchedulerTest()
60 : task_runner_(new base::TestSimpleTaskRunner()),
62 &gcm_driver_
, kFakeEnrollmentDomain
, kFakeDeviceId
, task_runner_
) {
65 void SetUp() override
{
66 // Swap out the DeviceSettingsProvider with our stub settings provider
67 // so we can set values for the heartbeat frequency.
68 chromeos::CrosSettings
* cros_settings
= chromeos::CrosSettings::Get();
69 device_settings_provider_
=
70 cros_settings
->GetProvider(chromeos::kReportDeviceVersionInfo
);
71 EXPECT_TRUE(device_settings_provider_
);
73 cros_settings
->RemoveSettingsProvider(device_settings_provider_
));
74 cros_settings
->AddSettingsProvider(&stub_settings_provider_
);
77 void TearDown() override
{
78 content::RunAllBlockingPoolTasksUntilIdle();
79 // Restore the real DeviceSettingsProvider.
80 chromeos::CrosSettings
* cros_settings
= chromeos::CrosSettings::Get();
81 EXPECT_TRUE(cros_settings
->RemoveSettingsProvider(
82 &stub_settings_provider_
));
83 cros_settings
->AddSettingsProvider(device_settings_provider_
);
86 void CheckPendingTaskDelay(base::Time last_heartbeat
,
87 base::TimeDelta expected_delay
) {
88 EXPECT_FALSE(last_heartbeat
.is_null());
89 base::Time now
= base::Time::NowFromSystemTime();
90 EXPECT_GE(now
, last_heartbeat
);
91 base::TimeDelta actual_delay
= task_runner_
->NextPendingTaskDelay();
93 // NextPendingTaskDelay() returns the exact original delay value the task
94 // was posted with. The heartbeat task would have been calculated to fire at
95 // |last_heartbeat| + |expected_delay|, but we don't know the exact time
96 // when the task was posted (if it was a couple of milliseconds after
97 // |last_heartbeat|, then |actual_delay| would be a couple of milliseconds
98 // smaller than |expected_delay|.
100 // We do know that the task was posted sometime between |last_heartbeat|
101 // and |now|, so we know that 0 <= |expected_delay| - |actual_delay| <=
102 // |now| - |last_heartbeat|.
103 base::TimeDelta delta
= expected_delay
- actual_delay
;
104 EXPECT_LE(base::TimeDelta(), delta
);
105 EXPECT_GE(now
- last_heartbeat
, delta
);
108 base::MessageLoop loop_
;
110 // Helpers used to mock out cros settings.
111 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_
;
112 chromeos::ScopedTestCrosSettings test_cros_settings_
;
113 chromeos::CrosSettingsProvider
* device_settings_provider_
;
114 chromeos::StubCrosSettingsProvider stub_settings_provider_
;
116 MockGCMDriver gcm_driver_
;
118 // TaskRunner used to run individual tests.
119 scoped_refptr
<base::TestSimpleTaskRunner
> task_runner_
;
121 // The HeartbeatScheduler instance under test.
122 policy::HeartbeatScheduler scheduler_
;
125 TEST_F(HeartbeatSchedulerTest
, Basic
) {
126 // Just makes sure we can spin up and shutdown the scheduler with
127 // heartbeats disabled.
128 chromeos::CrosSettings::Get()->SetBoolean(
129 chromeos::kHeartbeatEnabled
, false);
130 ASSERT_TRUE(task_runner_
->GetPendingTasks().empty());
133 TEST_F(HeartbeatSchedulerTest
, PermanentlyFailedGCMRegistration
) {
134 // If heartbeats are enabled, we should register with GCMDriver.
135 EXPECT_CALL(gcm_driver_
, RegisterImpl(kHeartbeatGCMAppID
, _
));
136 chromeos::CrosSettings::Get()->SetBoolean(
137 chromeos::kHeartbeatEnabled
, true);
138 gcm_driver_
.CompleteRegistration(
139 kHeartbeatGCMAppID
, gcm::GCMClient::GCM_DISABLED
);
141 // There should be no heartbeat tasks pending, because registration failed.
142 ASSERT_TRUE(task_runner_
->GetPendingTasks().empty());
145 TEST_F(HeartbeatSchedulerTest
, TemporarilyFailedGCMRegistration
) {
146 EXPECT_CALL(gcm_driver_
, RegisterImpl(kHeartbeatGCMAppID
, _
));
147 chromeos::CrosSettings::Get()->SetBoolean(
148 chromeos::kHeartbeatEnabled
, true);
149 gcm_driver_
.CompleteRegistration(
150 kHeartbeatGCMAppID
, gcm::GCMClient::SERVER_ERROR
);
151 testing::Mock::VerifyAndClearExpectations(&gcm_driver_
);
153 // Should have a pending task to try registering again.
154 ASSERT_FALSE(task_runner_
->GetPendingTasks().empty());
155 EXPECT_CALL(gcm_driver_
, RegisterImpl(kHeartbeatGCMAppID
, _
));
156 task_runner_
->RunPendingTasks();
157 testing::Mock::VerifyAndClearExpectations(&gcm_driver_
);
159 // Once we have successfully registered, we should send a heartbeat.
160 EXPECT_CALL(gcm_driver_
, SendImpl(kHeartbeatGCMAppID
, _
, _
));
161 gcm_driver_
.CompleteRegistration(
162 kHeartbeatGCMAppID
, gcm::GCMClient::SUCCESS
);
163 task_runner_
->RunPendingTasks();
166 TEST_F(HeartbeatSchedulerTest
, ChangeHeartbeatFrequency
) {
167 EXPECT_CALL(gcm_driver_
, RegisterImpl(kHeartbeatGCMAppID
, _
));
168 chromeos::CrosSettings::Get()->SetBoolean(
169 chromeos::kHeartbeatEnabled
, true);
170 gcm_driver_
.CompleteRegistration(
171 kHeartbeatGCMAppID
, gcm::GCMClient::SUCCESS
);
173 EXPECT_EQ(1U, task_runner_
->GetPendingTasks().size());
174 // Should have a heartbeat task posted with zero delay on startup.
175 EXPECT_EQ(base::TimeDelta(), task_runner_
->NextPendingTaskDelay());
176 testing::Mock::VerifyAndClearExpectations(&gcm_driver_
);
178 const int new_delay
= 1234*1000; // 1234 seconds.
179 chromeos::CrosSettings::Get()->SetInteger(chromeos::kHeartbeatFrequency
,
181 // Now run pending heartbeat task, should send a heartbeat.
182 gcm::GCMClient::OutgoingMessage message
;
183 EXPECT_CALL(gcm_driver_
, SendImpl(kHeartbeatGCMAppID
, _
, _
))
184 .WillOnce(SaveArg
<2>(&message
));
185 task_runner_
->RunPendingTasks();
186 EXPECT_TRUE(task_runner_
->GetPendingTasks().empty());
188 // Complete sending a message - we should queue up the next heartbeat
189 // even if the previous attempt failed.
190 gcm_driver_
.CompleteSend(
191 kHeartbeatGCMAppID
, message
.id
, gcm::GCMClient::SERVER_ERROR
);
192 EXPECT_EQ(1U, task_runner_
->GetPendingTasks().size());
193 CheckPendingTaskDelay(scheduler_
.last_heartbeat(),
194 base::TimeDelta::FromMilliseconds(new_delay
));
197 TEST_F(HeartbeatSchedulerTest
, DisableHeartbeats
) {
198 // Makes sure that we can disable heartbeats on the fly.
199 EXPECT_CALL(gcm_driver_
, RegisterImpl(kHeartbeatGCMAppID
, _
));
200 chromeos::CrosSettings::Get()->SetBoolean(
201 chromeos::kHeartbeatEnabled
, true);
202 gcm::GCMClient::OutgoingMessage message
;
203 EXPECT_CALL(gcm_driver_
, SendImpl(kHeartbeatGCMAppID
, _
, _
))
204 .WillOnce(SaveArg
<2>(&message
));
205 gcm_driver_
.CompleteRegistration(
206 kHeartbeatGCMAppID
, gcm::GCMClient::SUCCESS
);
207 // Should have a heartbeat task posted.
208 EXPECT_EQ(1U, task_runner_
->GetPendingTasks().size());
209 task_runner_
->RunPendingTasks();
211 // Complete sending a message - we should queue up the next heartbeat.
212 gcm_driver_
.CompleteSend(
213 kHeartbeatGCMAppID
, message
.id
, gcm::GCMClient::SUCCESS
);
215 // Should have a new heartbeat task posted.
216 ASSERT_EQ(1U, task_runner_
->GetPendingTasks().size());
217 CheckPendingTaskDelay(
218 scheduler_
.last_heartbeat(),
219 base::TimeDelta::FromMilliseconds(
220 policy::HeartbeatScheduler::kDefaultHeartbeatIntervalMs
));
221 testing::Mock::VerifyAndClearExpectations(&gcm_driver_
);
223 // Now disable heartbeats. Should get no more heartbeats sent.
224 chromeos::CrosSettings::Get()->SetBoolean(
225 chromeos::kHeartbeatEnabled
, false);
226 task_runner_
->RunPendingTasks();
227 EXPECT_TRUE(task_runner_
->GetPendingTasks().empty());
230 TEST_F(HeartbeatSchedulerTest
, CheckMessageContents
) {
231 gcm::GCMClient::OutgoingMessage message
;
232 EXPECT_CALL(gcm_driver_
, RegisterImpl(kHeartbeatGCMAppID
, _
));
233 EXPECT_CALL(gcm_driver_
, SendImpl(kHeartbeatGCMAppID
, _
, _
))
234 .WillOnce(SaveArg
<2>(&message
));
235 chromeos::CrosSettings::Get()->SetBoolean(
236 chromeos::kHeartbeatEnabled
, true);
237 gcm_driver_
.CompleteRegistration(
238 kHeartbeatGCMAppID
, gcm::GCMClient::SUCCESS
);
239 task_runner_
->RunPendingTasks();
241 // Heartbeats should have a time-to-live equivalent to the heartbeat frequency
242 // so we don't have more than one heartbeat queued at a time.
243 EXPECT_EQ(policy::HeartbeatScheduler::kDefaultHeartbeatIntervalMs
/1000,
244 message
.time_to_live
);
246 // Check the values in the message payload.
247 EXPECT_EQ("hb", message
.data
["type"]);
249 EXPECT_TRUE(base::StringToInt64(message
.data
["timestamp"], ×tamp
));
250 EXPECT_EQ(kFakeEnrollmentDomain
, message
.data
["domain_name"]);
251 EXPECT_EQ(kFakeDeviceId
, message
.data
["device_id"]);