1 // Copyright 2014 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 "google_apis/gcm/engine/heartbeat_manager.h"
8 #include "base/bind_helpers.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/test/test_simple_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/time/time.h"
13 #include "base/timer/timer.h"
14 #include "google_apis/gcm/protocol/mcs.pb.h"
15 #include "testing/gtest/include/gtest/gtest.h"
21 mcs_proto::HeartbeatConfig
BuildHeartbeatConfig(int interval_ms
) {
22 mcs_proto::HeartbeatConfig config
;
23 config
.set_interval_ms(interval_ms
);
27 class TestHeartbeatManager
: public HeartbeatManager
{
29 TestHeartbeatManager() {}
30 ~TestHeartbeatManager() override
{}
32 // Bypass the heartbeat timer, and send the heartbeat now.
33 void TriggerHearbeat();
35 // Check for a missed heartbeat now.
36 void TriggerMissedHeartbeatCheck();
39 void TestHeartbeatManager::TriggerHearbeat() {
40 OnHeartbeatTriggered();
43 void TestHeartbeatManager::TriggerMissedHeartbeatCheck() {
44 CheckForMissedHeartbeat();
47 class HeartbeatManagerTest
: public testing::Test
{
49 HeartbeatManagerTest();
50 ~HeartbeatManagerTest() override
{}
52 TestHeartbeatManager
* manager() const { return manager_
.get(); }
53 int heartbeats_sent() const { return heartbeats_sent_
; }
54 int reconnects_triggered() const { return reconnects_triggered_
; }
56 // Starts the heartbeat manager.
60 // Helper functions for verifying heartbeat manager effects.
61 void SendHeartbeatClosure();
62 void TriggerReconnectClosure(ConnectionFactory::ConnectionResetReason reason
);
64 scoped_ptr
<TestHeartbeatManager
> manager_
;
67 int reconnects_triggered_
;
69 scoped_refptr
<base::TestSimpleTaskRunner
> task_runner_
;
70 base::ThreadTaskRunnerHandle task_runner_handle_
;
73 HeartbeatManagerTest::HeartbeatManagerTest()
74 : manager_(new TestHeartbeatManager()),
76 reconnects_triggered_(0),
77 task_runner_(new base::TestSimpleTaskRunner()),
78 task_runner_handle_(task_runner_
) {
81 void HeartbeatManagerTest::StartManager() {
82 manager_
->Start(base::Bind(&HeartbeatManagerTest::SendHeartbeatClosure
,
83 base::Unretained(this)),
84 base::Bind(&HeartbeatManagerTest::TriggerReconnectClosure
,
85 base::Unretained(this)));
88 void HeartbeatManagerTest::SendHeartbeatClosure() {
92 void HeartbeatManagerTest::TriggerReconnectClosure(
93 ConnectionFactory::ConnectionResetReason reason
) {
94 reconnects_triggered_
++;
97 // Basic initialization. No heartbeat should be pending.
98 TEST_F(HeartbeatManagerTest
, Init
) {
99 EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
102 // Acknowledging a heartbeat before starting the manager should have no effect.
103 TEST_F(HeartbeatManagerTest
, AckBeforeStart
) {
104 manager()->OnHeartbeatAcked();
105 EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
108 // Starting the manager should start the heartbeat timer.
109 TEST_F(HeartbeatManagerTest
, Start
) {
111 EXPECT_GT(manager()->GetNextHeartbeatTime(), base::TimeTicks::Now());
112 EXPECT_EQ(0, heartbeats_sent());
113 EXPECT_EQ(0, reconnects_triggered());
116 // Acking the heartbeat should trigger a new heartbeat timer.
117 TEST_F(HeartbeatManagerTest
, AckedHeartbeat
) {
119 manager()->TriggerHearbeat();
120 base::TimeTicks heartbeat
= manager()->GetNextHeartbeatTime();
121 EXPECT_GT(heartbeat
, base::TimeTicks::Now());
122 EXPECT_EQ(1, heartbeats_sent());
123 EXPECT_EQ(0, reconnects_triggered());
125 manager()->OnHeartbeatAcked();
126 EXPECT_LT(heartbeat
, manager()->GetNextHeartbeatTime());
127 EXPECT_EQ(1, heartbeats_sent());
128 EXPECT_EQ(0, reconnects_triggered());
130 manager()->TriggerHearbeat();
131 EXPECT_EQ(2, heartbeats_sent());
132 EXPECT_EQ(0, reconnects_triggered());
135 // Trigger a heartbeat when one was outstanding should reset the connection.
136 TEST_F(HeartbeatManagerTest
, UnackedHeartbeat
) {
138 manager()->TriggerHearbeat();
139 EXPECT_EQ(1, heartbeats_sent());
140 EXPECT_EQ(0, reconnects_triggered());
142 manager()->TriggerHearbeat();
143 EXPECT_EQ(1, heartbeats_sent());
144 EXPECT_EQ(1, reconnects_triggered());
147 // Updating the heartbeat interval before starting should result in the new
148 // interval being used at Start time.
149 TEST_F(HeartbeatManagerTest
, UpdateIntervalThenStart
) {
150 const int kIntervalMs
= 60 * 1000; // 60 seconds.
151 manager()->UpdateHeartbeatConfig(BuildHeartbeatConfig(kIntervalMs
));
152 EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
154 EXPECT_LE(manager()->GetNextHeartbeatTime() - base::TimeTicks::Now(),
155 base::TimeDelta::FromMilliseconds(kIntervalMs
));
158 // Updating the heartbeat interval after starting should only use the new
159 // interval on the next heartbeat.
160 TEST_F(HeartbeatManagerTest
, StartThenUpdateInterval
) {
161 const int kIntervalMs
= 60 * 1000; // 60 seconds.
163 base::TimeTicks heartbeat
= manager()->GetNextHeartbeatTime();
164 EXPECT_GT(heartbeat
- base::TimeTicks::Now(),
165 base::TimeDelta::FromMilliseconds(kIntervalMs
));
167 // Updating the interval should not affect an outstanding heartbeat.
168 manager()->UpdateHeartbeatConfig(BuildHeartbeatConfig(kIntervalMs
));
169 EXPECT_EQ(heartbeat
, manager()->GetNextHeartbeatTime());
171 // Triggering and acking the heartbeat should result in a heartbeat being
172 // posted with the new interval.
173 manager()->TriggerHearbeat();
174 manager()->OnHeartbeatAcked();
176 EXPECT_LE(manager()->GetNextHeartbeatTime() - base::TimeTicks::Now(),
177 base::TimeDelta::FromMilliseconds(kIntervalMs
));
178 EXPECT_NE(heartbeat
, manager()->GetNextHeartbeatTime());
181 // Updating the timer used for heartbeats before starting should not start the
183 TEST_F(HeartbeatManagerTest
, UpdateTimerBeforeStart
) {
184 manager()->UpdateHeartbeatTimer(
185 make_scoped_ptr(new base::Timer(true, false)));
186 EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
189 // Updating the timer used for heartbeats after starting should restart the
190 // timer but not increase the heartbeat time by more than a millisecond.
191 TEST_F(HeartbeatManagerTest
, UpdateTimerAfterStart
) {
193 base::TimeTicks heartbeat
= manager()->GetNextHeartbeatTime();
195 manager()->UpdateHeartbeatTimer(
196 make_scoped_ptr(new base::Timer(true, false)));
197 EXPECT_LT(manager()->GetNextHeartbeatTime() - heartbeat
,
198 base::TimeDelta::FromMilliseconds(5));
201 // Stopping the manager should reset the heartbeat timer.
202 TEST_F(HeartbeatManagerTest
, Stop
) {
204 EXPECT_GT(manager()->GetNextHeartbeatTime(), base::TimeTicks::Now());
207 EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
210 // Simulate missing a heartbeat by manually invoking the check method. The
211 // heartbeat should only be triggered once, and only if the heartbeat timer
212 // is running. Because the period is several minutes, none should fire.
213 TEST_F(HeartbeatManagerTest
, MissedHeartbeat
) {
214 // Do nothing while stopped.
215 manager()->TriggerMissedHeartbeatCheck();
217 EXPECT_EQ(0, heartbeats_sent());
219 // Do nothing before the period is reached.
220 manager()->TriggerMissedHeartbeatCheck();
221 EXPECT_EQ(0, heartbeats_sent());
224 // Sets the client hearbeat interval and checks that it is picked up by the
226 TEST_F(HeartbeatManagerTest
, SetClientHeartbeatInterval
) {
227 const int kIntervalMs
= 180 * 1000; // 180 seconds.
229 manager()->TriggerHearbeat();
230 manager()->OnHeartbeatAcked();
232 base::TimeTicks heartbeat
= manager()->GetNextHeartbeatTime();
233 EXPECT_GT(heartbeat
- base::TimeTicks::Now(),
234 base::TimeDelta::FromMilliseconds(kIntervalMs
));
236 manager()->SetClientHeartbeatIntervalMs(kIntervalMs
);
237 EXPECT_EQ(1, reconnects_triggered());
239 // Triggering and acking the heartbeat should result in a heartbeat being
240 // posted with the new interval.
241 manager()->TriggerHearbeat();
242 manager()->OnHeartbeatAcked();
244 EXPECT_LE(manager()->GetNextHeartbeatTime() - base::TimeTicks::Now(),
245 base::TimeDelta::FromMilliseconds(kIntervalMs
));
246 EXPECT_GT(heartbeat
, manager()->GetNextHeartbeatTime());
248 const int kLongerIntervalMs
= 2 * kIntervalMs
;
249 // Updating the interval should not affect an outstanding heartbeat.
250 manager()->SetClientHeartbeatIntervalMs(kLongerIntervalMs
);
251 // No extra reconnects happen here, because the heartbeat is longer.
252 EXPECT_EQ(1, reconnects_triggered());
254 // Triggering and acking the heartbeat should result in a heartbeat being
255 // posted with the old, shorter interval.
256 manager()->TriggerHearbeat();
257 manager()->OnHeartbeatAcked();
259 EXPECT_LE(manager()->GetNextHeartbeatTime() - base::TimeTicks::Now(),
260 base::TimeDelta::FromMilliseconds(kIntervalMs
));
263 // Verifies that setting the client interval too low or too high will set it to
264 // a value within a reasonable scope.
265 TEST_F(HeartbeatManagerTest
, ClientIntervalInvalid
) {
266 // Less than min value.
267 int interval_ms
= manager()->GetMinClientHeartbeatIntervalMs() - 60 * 1000;
268 manager()->SetClientHeartbeatIntervalMs(interval_ms
);
269 EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
271 base::TimeDelta till_heartbeat
= manager()->GetNextHeartbeatTime() -
272 base::TimeTicks::Now();
273 EXPECT_GT(till_heartbeat
, base::TimeDelta::FromMilliseconds(
274 manager()->GetMinClientHeartbeatIntervalMs()));
275 EXPECT_LE(till_heartbeat
, base::TimeDelta::FromMilliseconds(
276 manager()->GetMaxClientHeartbeatIntervalMs()));
278 // More than max value.
279 interval_ms
= manager()->GetMaxClientHeartbeatIntervalMs() + 60 * 1000;
280 // Triggering and acking the heartbeat should result in a heartbeat being
281 // posted with the new interval.
282 manager()->TriggerHearbeat();
283 manager()->OnHeartbeatAcked();
285 till_heartbeat
= manager()->GetNextHeartbeatTime() - base::TimeTicks::Now();
286 EXPECT_LE(till_heartbeat
, base::TimeDelta::FromMilliseconds(
287 manager()->GetMaxClientHeartbeatIntervalMs()));