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.
4
5#include "google_apis/gcm/engine/heartbeat_manager.h"
6
7#include "base/message_loop/message_loop.h"
8#include "base/time/time.h"
9#include "google_apis/gcm/protocol/mcs.pb.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12namespace gcm {
13
14namespace {
15
16mcs_proto::HeartbeatConfig BuildHeartbeatConfig(int interval_ms) {
17  mcs_proto::HeartbeatConfig config;
18  config.set_interval_ms(interval_ms);
19  return config;
20}
21
22class TestHeartbeatManager : public HeartbeatManager {
23 public:
24  TestHeartbeatManager() {}
25  virtual ~TestHeartbeatManager() {}
26
27  // Bypass the heartbeat timer, and send the heartbeat now.
28  void TriggerHearbeat();
29};
30
31void TestHeartbeatManager::TriggerHearbeat() {
32  OnHeartbeatTriggered();
33}
34
35class HeartbeatManagerTest : public testing::Test {
36 public:
37  HeartbeatManagerTest();
38  virtual ~HeartbeatManagerTest() {}
39
40  TestHeartbeatManager* manager() const { return manager_.get(); }
41  int heartbeats_sent() const { return heartbeats_sent_; }
42  int reconnects_triggered() const { return reconnects_triggered_; }
43
44  // Starts the heartbeat manager.
45  void StartManager();
46
47 private:
48  // Helper functions for verifying heartbeat manager effects.
49  void SendHeartbeatClosure();
50  void TriggerReconnectClosure();
51
52  scoped_ptr<TestHeartbeatManager> manager_;
53
54  int heartbeats_sent_;
55  int reconnects_triggered_;
56
57  base::MessageLoop message_loop_;
58};
59
60HeartbeatManagerTest::HeartbeatManagerTest()
61    : manager_(new TestHeartbeatManager()),
62      heartbeats_sent_(0),
63      reconnects_triggered_(0) {
64}
65
66void HeartbeatManagerTest::StartManager() {
67  manager_->Start(base::Bind(&HeartbeatManagerTest::SendHeartbeatClosure,
68                             base::Unretained(this)),
69                  base::Bind(&HeartbeatManagerTest::TriggerReconnectClosure,
70                             base::Unretained(this)));
71}
72
73void HeartbeatManagerTest::SendHeartbeatClosure() {
74  heartbeats_sent_++;
75}
76
77void HeartbeatManagerTest::TriggerReconnectClosure() {
78  reconnects_triggered_++;
79}
80
81// Basic initialization. No heartbeat should be pending.
82TEST_F(HeartbeatManagerTest, Init) {
83  EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
84}
85
86// Acknowledging a heartbeat before starting the manager should have no effect.
87TEST_F(HeartbeatManagerTest, AckBeforeStart) {
88  manager()->OnHeartbeatAcked();
89  EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
90}
91
92// Starting the manager should start the heartbeat timer.
93TEST_F(HeartbeatManagerTest, Start) {
94  StartManager();
95  EXPECT_GT(manager()->GetNextHeartbeatTime(), base::TimeTicks::Now());
96  EXPECT_EQ(0, heartbeats_sent());
97  EXPECT_EQ(0, reconnects_triggered());
98}
99
100// Acking the heartbeat should trigger a new heartbeat timer.
101TEST_F(HeartbeatManagerTest, AckedHeartbeat) {
102  StartManager();
103  manager()->TriggerHearbeat();
104  base::TimeTicks heartbeat = manager()->GetNextHeartbeatTime();
105  EXPECT_GT(heartbeat, base::TimeTicks::Now());
106  EXPECT_EQ(1, heartbeats_sent());
107  EXPECT_EQ(0, reconnects_triggered());
108
109  manager()->OnHeartbeatAcked();
110  EXPECT_LT(heartbeat, manager()->GetNextHeartbeatTime());
111  EXPECT_EQ(1, heartbeats_sent());
112  EXPECT_EQ(0, reconnects_triggered());
113
114  manager()->TriggerHearbeat();
115  EXPECT_EQ(2, heartbeats_sent());
116  EXPECT_EQ(0, reconnects_triggered());
117}
118
119// Trigger a heartbeat when one was outstanding should reset the connection.
120TEST_F(HeartbeatManagerTest, UnackedHeartbeat) {
121  StartManager();
122  manager()->TriggerHearbeat();
123  EXPECT_EQ(1, heartbeats_sent());
124  EXPECT_EQ(0, reconnects_triggered());
125
126  manager()->TriggerHearbeat();
127  EXPECT_EQ(1, heartbeats_sent());
128  EXPECT_EQ(1, reconnects_triggered());
129}
130
131// Updating the heartbeat interval before starting should result in the new
132// interval being used at Start time.
133TEST_F(HeartbeatManagerTest, UpdateIntervalThenStart) {
134  const int kIntervalMs = 60 * 1000;  // 60 seconds.
135  manager()->UpdateHeartbeatConfig(BuildHeartbeatConfig(kIntervalMs));
136  EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
137  StartManager();
138  EXPECT_LE(manager()->GetNextHeartbeatTime() - base::TimeTicks::Now(),
139            base::TimeDelta::FromMilliseconds(kIntervalMs));
140}
141
142// Updating the heartbeat interval after starting should only use the new
143// interval on the next heartbeat.
144TEST_F(HeartbeatManagerTest, StartThenUpdateInterval) {
145  const int kIntervalMs = 60 * 1000;  // 60 seconds.
146  StartManager();
147  base::TimeTicks heartbeat = manager()->GetNextHeartbeatTime();
148  EXPECT_GT(heartbeat - base::TimeTicks::Now(),
149            base::TimeDelta::FromMilliseconds(kIntervalMs));
150
151  // Updating the interval should not affect an outstanding heartbeat.
152  manager()->UpdateHeartbeatConfig(BuildHeartbeatConfig(kIntervalMs));
153  EXPECT_EQ(heartbeat, manager()->GetNextHeartbeatTime());
154
155  // Triggering and acking the heartbeat should result in a heartbeat being
156  // posted with the new interval.
157  manager()->TriggerHearbeat();
158  manager()->OnHeartbeatAcked();
159
160  EXPECT_LE(manager()->GetNextHeartbeatTime() - base::TimeTicks::Now(),
161            base::TimeDelta::FromMilliseconds(kIntervalMs));
162  EXPECT_NE(heartbeat, manager()->GetNextHeartbeatTime());
163}
164
165// Stopping the manager should reset the heartbeat timer.
166TEST_F(HeartbeatManagerTest, Stop) {
167  StartManager();
168  EXPECT_GT(manager()->GetNextHeartbeatTime(), base::TimeTicks::Now());
169
170  manager()->Stop();
171  EXPECT_TRUE(manager()->GetNextHeartbeatTime().is_null());
172}
173
174}  // namespace
175
176}  // namespace gcm
177