mcs_client_unittest.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
1// Copyright 2013 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/mcs_client.h"
6
7#include "base/command_line.h"
8#include "base/files/scoped_temp_dir.h"
9#include "base/message_loop/message_loop.h"
10#include "base/run_loop.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/test/simple_test_clock.h"
13#include "components/os_crypt/os_crypt_switches.h"
14#include "google_apis/gcm/base/mcs_util.h"
15#include "google_apis/gcm/engine/fake_connection_factory.h"
16#include "google_apis/gcm/engine/fake_connection_handler.h"
17#include "google_apis/gcm/engine/gcm_store_impl.h"
18#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
19#include "testing/gtest/include/gtest/gtest.h"
20
21namespace gcm {
22
23namespace {
24
25const uint64 kAndroidId = 54321;
26const uint64 kSecurityToken = 12345;
27
28// Number of messages to send when testing batching.
29// Note: must be even for tests that split batches in half.
30const int kMessageBatchSize = 6;
31
32// The number of unacked messages the client will receive before sending a
33// stream ack.
34// TODO(zea): get this (and other constants) directly from the mcs client.
35const int kAckLimitSize = 10;
36
37// TTL value for reliable messages.
38const int kTTLValue = 5 * 60;  // 5 minutes.
39
40// Helper for building arbitrary data messages.
41MCSMessage BuildDataMessage(const std::string& from,
42                            const std::string& category,
43                            const std::string& message_id,
44                            int last_stream_id_received,
45                            const std::string& persistent_id,
46                            int ttl,
47                            uint64 sent,
48                            int queued,
49                            const std::string& token,
50                            const uint64& user_id) {
51  mcs_proto::DataMessageStanza data_message;
52  data_message.set_id(message_id);
53  data_message.set_from(from);
54  data_message.set_category(category);
55  data_message.set_last_stream_id_received(last_stream_id_received);
56  if (!persistent_id.empty())
57    data_message.set_persistent_id(persistent_id);
58  data_message.set_ttl(ttl);
59  data_message.set_sent(sent);
60  data_message.set_queued(queued);
61  data_message.set_token(token);
62  data_message.set_device_user_id(user_id);
63  return MCSMessage(kDataMessageStanzaTag, data_message);
64}
65
66// MCSClient with overriden exposed persistent id logic.
67class TestMCSClient : public MCSClient {
68 public:
69  TestMCSClient(base::Clock* clock,
70                ConnectionFactory* connection_factory,
71                GCMStore* gcm_store,
72                gcm::GCMStatsRecorder* recorder)
73    : MCSClient("", clock, connection_factory, gcm_store, recorder),
74      next_id_(0) {
75  }
76
77  virtual std::string GetNextPersistentId() OVERRIDE {
78    return base::UintToString(++next_id_);
79  }
80
81 private:
82  uint32 next_id_;
83};
84
85class MCSClientTest : public testing::Test {
86 public:
87  MCSClientTest();
88  virtual ~MCSClientTest();
89
90  virtual void SetUp() OVERRIDE;
91
92  void BuildMCSClient();
93  void InitializeClient();
94  void StoreCredentials();
95  void LoginClient(const std::vector<std::string>& acknowledged_ids);
96
97  base::SimpleTestClock* clock() { return &clock_; }
98  TestMCSClient* mcs_client() const { return mcs_client_.get(); }
99  FakeConnectionFactory* connection_factory() {
100    return &connection_factory_;
101  }
102  bool init_success() const { return init_success_; }
103  uint64 restored_android_id() const { return restored_android_id_; }
104  uint64 restored_security_token() const { return restored_security_token_; }
105  MCSMessage* received_message() const { return received_message_.get(); }
106  std::string sent_message_id() const { return sent_message_id_;}
107  MCSClient::MessageSendStatus message_send_status() const {
108    return message_send_status_;
109  }
110
111  void SetDeviceCredentialsCallback(bool success);
112
113  FakeConnectionHandler* GetFakeHandler() const;
114
115  void WaitForMCSEvent();
116  void PumpLoop();
117
118 private:
119  void ErrorCallback();
120  void MessageReceivedCallback(const MCSMessage& message);
121  void MessageSentCallback(int64 user_serial_number,
122                           const std::string& app_id,
123                           const std::string& message_id,
124                           MCSClient::MessageSendStatus status);
125
126  base::SimpleTestClock clock_;
127
128  base::ScopedTempDir temp_directory_;
129  base::MessageLoop message_loop_;
130  scoped_ptr<base::RunLoop> run_loop_;
131  scoped_ptr<GCMStore> gcm_store_;
132
133  FakeConnectionFactory connection_factory_;
134  scoped_ptr<TestMCSClient> mcs_client_;
135  bool init_success_;
136  uint64 restored_android_id_;
137  uint64 restored_security_token_;
138  scoped_ptr<MCSMessage> received_message_;
139  std::string sent_message_id_;
140  MCSClient::MessageSendStatus message_send_status_;
141
142  gcm::GCMStatsRecorder recorder_;
143};
144
145MCSClientTest::MCSClientTest()
146    : run_loop_(new base::RunLoop()),
147      init_success_(true),
148      restored_android_id_(0),
149      restored_security_token_(0),
150      message_send_status_(MCSClient::SENT) {
151  EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
152  run_loop_.reset(new base::RunLoop());
153
154  // Advance the clock to a non-zero time.
155  clock_.Advance(base::TimeDelta::FromSeconds(1));
156}
157
158MCSClientTest::~MCSClientTest() {}
159
160void MCSClientTest::SetUp() {
161  testing::Test::SetUp();
162#if defined(OS_MACOSX)
163  base::CommandLine::ForCurrentProcess()->AppendSwitch(
164      os_crypt::switches::kUseMockKeychain);
165#endif  // OS_MACOSX
166}
167
168void MCSClientTest::BuildMCSClient() {
169  gcm_store_.reset(new GCMStoreImpl(temp_directory_.path(),
170                                    message_loop_.message_loop_proxy()));
171  mcs_client_.reset(new TestMCSClient(&clock_,
172                                      &connection_factory_,
173                                      gcm_store_.get(),
174                                      &recorder_));
175}
176
177void MCSClientTest::InitializeClient() {
178  gcm_store_->Load(base::Bind(
179      &MCSClient::Initialize,
180      base::Unretained(mcs_client_.get()),
181      base::Bind(&MCSClientTest::ErrorCallback,
182                 base::Unretained(this)),
183      base::Bind(&MCSClientTest::MessageReceivedCallback,
184                 base::Unretained(this)),
185      base::Bind(&MCSClientTest::MessageSentCallback, base::Unretained(this))));
186  run_loop_->RunUntilIdle();
187  run_loop_.reset(new base::RunLoop());
188}
189
190void MCSClientTest::LoginClient(
191    const std::vector<std::string>& acknowledged_ids) {
192  scoped_ptr<mcs_proto::LoginRequest> login_request =
193      BuildLoginRequest(kAndroidId, kSecurityToken, "");
194  for (size_t i = 0; i < acknowledged_ids.size(); ++i)
195    login_request->add_received_persistent_id(acknowledged_ids[i]);
196  GetFakeHandler()->ExpectOutgoingMessage(
197      MCSMessage(kLoginRequestTag,
198                 login_request.PassAs<const google::protobuf::MessageLite>()));
199  mcs_client_->Login(kAndroidId, kSecurityToken);
200  run_loop_->Run();
201  run_loop_.reset(new base::RunLoop());
202}
203
204void MCSClientTest::StoreCredentials() {
205  gcm_store_->SetDeviceCredentials(
206      kAndroidId, kSecurityToken,
207      base::Bind(&MCSClientTest::SetDeviceCredentialsCallback,
208                 base::Unretained(this)));
209  run_loop_->Run();
210  run_loop_.reset(new base::RunLoop());
211}
212
213FakeConnectionHandler* MCSClientTest::GetFakeHandler() const {
214  return reinterpret_cast<FakeConnectionHandler*>(
215      connection_factory_.GetConnectionHandler());
216}
217
218void MCSClientTest::WaitForMCSEvent() {
219  run_loop_->Run();
220  run_loop_.reset(new base::RunLoop());
221}
222
223void MCSClientTest::PumpLoop() {
224  run_loop_->RunUntilIdle();
225  run_loop_.reset(new base::RunLoop());
226}
227
228void MCSClientTest::ErrorCallback() {
229  init_success_ = false;
230  DVLOG(1) << "Error callback invoked, killing loop.";
231  run_loop_->Quit();
232}
233
234void MCSClientTest::MessageReceivedCallback(const MCSMessage& message) {
235  received_message_.reset(new MCSMessage(message));
236  DVLOG(1) << "Message received callback invoked, killing loop.";
237  run_loop_->Quit();
238}
239
240void MCSClientTest::MessageSentCallback(int64 user_serial_number,
241                                        const std::string& app_id,
242                                        const std::string& message_id,
243                                        MCSClient::MessageSendStatus status) {
244  DVLOG(1) << "Message sent callback invoked, killing loop.";
245  sent_message_id_ = message_id;
246  message_send_status_ = status;
247  run_loop_->Quit();
248}
249
250void MCSClientTest::SetDeviceCredentialsCallback(bool success) {
251  ASSERT_TRUE(success);
252  run_loop_->Quit();
253}
254
255// Initialize a new client.
256TEST_F(MCSClientTest, InitializeNew) {
257  BuildMCSClient();
258  InitializeClient();
259  EXPECT_TRUE(init_success());
260}
261
262// Initialize a new client, shut it down, then restart the client. Should
263// reload the existing device credentials.
264TEST_F(MCSClientTest, InitializeExisting) {
265  BuildMCSClient();
266  InitializeClient();
267  LoginClient(std::vector<std::string>());
268
269  // Rebuild the client, to reload from the GCM store.
270  StoreCredentials();
271  BuildMCSClient();
272  InitializeClient();
273  EXPECT_TRUE(init_success());
274}
275
276// Log in successfully to the MCS endpoint.
277TEST_F(MCSClientTest, LoginSuccess) {
278  BuildMCSClient();
279  InitializeClient();
280  LoginClient(std::vector<std::string>());
281  EXPECT_TRUE(connection_factory()->IsEndpointReachable());
282  EXPECT_TRUE(init_success());
283  ASSERT_TRUE(received_message());
284  EXPECT_EQ(kLoginResponseTag, received_message()->tag());
285}
286
287// Encounter a server error during the login attempt. Should trigger a
288// reconnect.
289TEST_F(MCSClientTest, FailLogin) {
290  BuildMCSClient();
291  InitializeClient();
292  GetFakeHandler()->set_fail_login(true);
293  connection_factory()->set_delay_reconnect(true);
294  LoginClient(std::vector<std::string>());
295  EXPECT_FALSE(connection_factory()->IsEndpointReachable());
296  EXPECT_FALSE(init_success());
297  EXPECT_FALSE(received_message());
298  EXPECT_TRUE(connection_factory()->reconnect_pending());
299}
300
301// Send a message without RMQ support.
302TEST_F(MCSClientTest, SendMessageNoRMQ) {
303  BuildMCSClient();
304  InitializeClient();
305  LoginClient(std::vector<std::string>());
306  MCSMessage message(
307      BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
308  GetFakeHandler()->ExpectOutgoingMessage(message);
309  mcs_client()->SendMessage(message);
310  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
311}
312
313// Send a message without RMQ support while disconnected. Message send should
314// fail immediately, invoking callback.
315TEST_F(MCSClientTest, SendMessageNoRMQWhileDisconnected) {
316  BuildMCSClient();
317  InitializeClient();
318
319  EXPECT_TRUE(sent_message_id().empty());
320  MCSMessage message(
321      BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
322  mcs_client()->SendMessage(message);
323
324  // Message sent callback should be invoked, but no message should actually
325  // be sent.
326  EXPECT_EQ("X", sent_message_id());
327  EXPECT_EQ(MCSClient::NO_CONNECTION_ON_ZERO_TTL, message_send_status());
328  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
329}
330
331// Send a message with RMQ support.
332TEST_F(MCSClientTest, SendMessageRMQ) {
333  BuildMCSClient();
334  InitializeClient();
335  LoginClient(std::vector<std::string>());
336  MCSMessage message(BuildDataMessage(
337      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
338  GetFakeHandler()->ExpectOutgoingMessage(message);
339  mcs_client()->SendMessage(message);
340  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
341}
342
343// Send a message with RMQ support while disconnected. On reconnect, the message
344// should be resent.
345TEST_F(MCSClientTest, SendMessageRMQWhileDisconnected) {
346  BuildMCSClient();
347  InitializeClient();
348  LoginClient(std::vector<std::string>());
349  GetFakeHandler()->set_fail_send(true);
350  MCSMessage message(BuildDataMessage(
351      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
352
353  // The initial (failed) send.
354  GetFakeHandler()->ExpectOutgoingMessage(message);
355  // The login request.
356  GetFakeHandler()->ExpectOutgoingMessage(
357      MCSMessage(
358          kLoginRequestTag,
359          BuildLoginRequest(kAndroidId, kSecurityToken, "").
360              PassAs<const google::protobuf::MessageLite>()));
361  // The second (re)send.
362  MCSMessage message2(BuildDataMessage(
363      "from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
364  GetFakeHandler()->ExpectOutgoingMessage(message2);
365  mcs_client()->SendMessage(message);
366  PumpLoop();         // Wait for the queuing to happen.
367  EXPECT_EQ(MCSClient::QUEUED, message_send_status());
368  EXPECT_FALSE(GetFakeHandler()->AllOutgoingMessagesReceived());
369  GetFakeHandler()->set_fail_send(false);
370  clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1));
371  connection_factory()->Connect();
372  WaitForMCSEvent();  // Wait for the login to finish.
373  PumpLoop();         // Wait for the send to happen.
374  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
375}
376
377// Send a message with RMQ support without receiving an acknowledgement. On
378// restart the message should be resent.
379TEST_F(MCSClientTest, SendMessageRMQOnRestart) {
380  BuildMCSClient();
381  InitializeClient();
382  LoginClient(std::vector<std::string>());
383  GetFakeHandler()->set_fail_send(true);
384  MCSMessage message(BuildDataMessage(
385      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
386
387  // The initial (failed) send.
388  GetFakeHandler()->ExpectOutgoingMessage(message);
389  GetFakeHandler()->set_fail_send(false);
390  mcs_client()->SendMessage(message);
391  PumpLoop();
392  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
393
394  // Rebuild the client, which should resend the old message.
395  StoreCredentials();
396  BuildMCSClient();
397  InitializeClient();
398
399  clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1));
400  MCSMessage message2(BuildDataMessage(
401      "from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
402  LoginClient(std::vector<std::string>());
403  GetFakeHandler()->ExpectOutgoingMessage(message2);
404  PumpLoop();
405  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
406}
407
408// Send messages with RMQ support, followed by receiving a stream ack. On
409// restart nothing should be recent.
410TEST_F(MCSClientTest, SendMessageRMQWithStreamAck) {
411  BuildMCSClient();
412  InitializeClient();
413  LoginClient(std::vector<std::string>());
414
415  // Send some messages.
416  for (int i = 1; i <= kMessageBatchSize; ++i) {
417    MCSMessage message(BuildDataMessage("from",
418                                        "category",
419                                        "X",
420                                        1,
421                                        base::IntToString(i),
422                                        kTTLValue,
423                                        1,
424                                        0,
425                                        "",
426                                        0));
427    GetFakeHandler()->ExpectOutgoingMessage(message);
428    mcs_client()->SendMessage(message);
429    PumpLoop();
430  }
431  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
432
433  // Receive the ack.
434  scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
435  ack->set_last_stream_id_received(kMessageBatchSize + 1);
436  GetFakeHandler()->ReceiveMessage(
437      MCSMessage(kIqStanzaTag,
438                 ack.PassAs<const google::protobuf::MessageLite>()));
439  WaitForMCSEvent();
440
441  // Reconnect and ensure no messages are resent.
442  StoreCredentials();
443  BuildMCSClient();
444  InitializeClient();
445  LoginClient(std::vector<std::string>());
446  PumpLoop();
447}
448
449// Send messages with RMQ support. On restart, receive a SelectiveAck with
450// the login response. No messages should be resent.
451TEST_F(MCSClientTest, SendMessageRMQAckOnReconnect) {
452  BuildMCSClient();
453  InitializeClient();
454  LoginClient(std::vector<std::string>());
455
456  // Send some messages.
457  std::vector<std::string> id_list;
458  for (int i = 1; i <= kMessageBatchSize; ++i) {
459    id_list.push_back(base::IntToString(i));
460    MCSMessage message(BuildDataMessage("from",
461                                        "category",
462                                        id_list.back(),
463                                        1,
464                                        id_list.back(),
465                                        kTTLValue,
466                                        1,
467                                        0,
468                                        "",
469                                        0));
470    GetFakeHandler()->ExpectOutgoingMessage(message);
471    mcs_client()->SendMessage(message);
472    PumpLoop();
473  }
474  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
475
476  // Rebuild the client, and receive an acknowledgment for the messages as
477  // part of the login response.
478  StoreCredentials();
479  BuildMCSClient();
480  InitializeClient();
481  LoginClient(std::vector<std::string>());
482  scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(id_list));
483  GetFakeHandler()->ReceiveMessage(
484      MCSMessage(kIqStanzaTag,
485                 ack.PassAs<const google::protobuf::MessageLite>()));
486  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
487}
488
489// Send messages with RMQ support. On restart, receive a SelectiveAck with
490// the login response that only acks some messages. The unacked messages should
491// be resent.
492TEST_F(MCSClientTest, SendMessageRMQPartialAckOnReconnect) {
493  BuildMCSClient();
494  InitializeClient();
495  LoginClient(std::vector<std::string>());
496
497  // Send some messages.
498  std::vector<std::string> id_list;
499  for (int i = 1; i <= kMessageBatchSize; ++i) {
500    id_list.push_back(base::IntToString(i));
501    MCSMessage message(BuildDataMessage("from",
502                                        "category",
503                                        id_list.back(),
504                                        1,
505                                        id_list.back(),
506                                        kTTLValue,
507                                        1,
508                                        0,
509                                        "",
510                                        0));
511    GetFakeHandler()->ExpectOutgoingMessage(message);
512    mcs_client()->SendMessage(message);
513    PumpLoop();
514  }
515  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
516
517  // Rebuild the client, and receive an acknowledgment for the messages as
518  // part of the login response.
519  StoreCredentials();
520  BuildMCSClient();
521  InitializeClient();
522  LoginClient(std::vector<std::string>());
523
524  std::vector<std::string> acked_ids, remaining_ids;
525  acked_ids.insert(acked_ids.end(),
526                   id_list.begin(),
527                   id_list.begin() + kMessageBatchSize / 2);
528  remaining_ids.insert(remaining_ids.end(),
529                       id_list.begin() + kMessageBatchSize / 2,
530                       id_list.end());
531  for (int i = 1; i <= kMessageBatchSize / 2; ++i) {
532    MCSMessage message(BuildDataMessage("from",
533                                        "category",
534                                        remaining_ids[i - 1],
535                                        2,
536                                        remaining_ids[i - 1],
537                                        kTTLValue,
538                                        1,
539                                        0,
540                                        "",
541                                        0));
542    GetFakeHandler()->ExpectOutgoingMessage(message);
543  }
544  scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(acked_ids));
545  GetFakeHandler()->ReceiveMessage(
546      MCSMessage(kIqStanzaTag,
547                 ack.PassAs<const google::protobuf::MessageLite>()));
548  WaitForMCSEvent();
549  PumpLoop();
550  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
551}
552
553// Receive some messages. On restart, the login request should contain the
554// appropriate acknowledged ids.
555TEST_F(MCSClientTest, AckOnLogin) {
556  BuildMCSClient();
557  InitializeClient();
558  LoginClient(std::vector<std::string>());
559
560  // Receive some messages.
561  std::vector<std::string> id_list;
562  for (int i = 1; i <= kMessageBatchSize; ++i) {
563    id_list.push_back(base::IntToString(i));
564    MCSMessage message(BuildDataMessage(
565        "from", "category", "X", 1, id_list.back(), kTTLValue, 1, 0, "", 0));
566    GetFakeHandler()->ReceiveMessage(message);
567    WaitForMCSEvent();
568    PumpLoop();
569  }
570
571  // Restart the client.
572  StoreCredentials();
573  BuildMCSClient();
574  InitializeClient();
575  LoginClient(id_list);
576}
577
578// Receive some messages. On the next send, the outgoing message should contain
579// the appropriate last stream id received field to ack the received messages.
580TEST_F(MCSClientTest, AckOnSend) {
581  BuildMCSClient();
582  InitializeClient();
583  LoginClient(std::vector<std::string>());
584
585  // Receive some messages.
586  std::vector<std::string> id_list;
587  for (int i = 1; i <= kMessageBatchSize; ++i) {
588    id_list.push_back(base::IntToString(i));
589    MCSMessage message(BuildDataMessage("from",
590                                        "category",
591                                        id_list.back(),
592                                        1,
593                                        id_list.back(),
594                                        kTTLValue,
595                                        1,
596                                        0,
597                                        "",
598                                        0));
599    GetFakeHandler()->ReceiveMessage(message);
600    PumpLoop();
601  }
602
603  // Trigger a message send, which should acknowledge via stream ack.
604  MCSMessage message(BuildDataMessage("from",
605                                      "category",
606                                      "X",
607                                      kMessageBatchSize + 1,
608                                      "1",
609                                      kTTLValue,
610                                      1,
611                                      0,
612                                      "",
613                                      0));
614  GetFakeHandler()->ExpectOutgoingMessage(message);
615  mcs_client()->SendMessage(message);
616  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
617}
618
619// Receive the ack limit in messages, which should trigger an automatic
620// stream ack. Receive a heartbeat to confirm the ack.
621TEST_F(MCSClientTest, AckWhenLimitReachedWithHeartbeat) {
622  BuildMCSClient();
623  InitializeClient();
624  LoginClient(std::vector<std::string>());
625
626  // The stream ack.
627  scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
628  ack->set_last_stream_id_received(kAckLimitSize + 1);
629  GetFakeHandler()->ExpectOutgoingMessage(
630      MCSMessage(kIqStanzaTag,
631                 ack.PassAs<const google::protobuf::MessageLite>()));
632
633  // Receive some messages.
634  std::vector<std::string> id_list;
635  for (int i = 1; i <= kAckLimitSize; ++i) {
636    id_list.push_back(base::IntToString(i));
637    MCSMessage message(BuildDataMessage("from",
638                                        "category",
639                                        id_list.back(),
640                                        1,
641                                        id_list.back(),
642                                        kTTLValue,
643                                        1,
644                                        0,
645                                        "",
646                                        0));
647    GetFakeHandler()->ReceiveMessage(message);
648    WaitForMCSEvent();
649    PumpLoop();
650  }
651  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
652
653  // Receive a heartbeat confirming the ack (and receive the heartbeat ack).
654  scoped_ptr<mcs_proto::HeartbeatPing> heartbeat(
655      new mcs_proto::HeartbeatPing());
656  heartbeat->set_last_stream_id_received(2);
657
658  scoped_ptr<mcs_proto::HeartbeatAck> heartbeat_ack(
659      new mcs_proto::HeartbeatAck());
660  heartbeat_ack->set_last_stream_id_received(kAckLimitSize + 2);
661  GetFakeHandler()->ExpectOutgoingMessage(
662      MCSMessage(kHeartbeatAckTag,
663                 heartbeat_ack.PassAs<const google::protobuf::MessageLite>()));
664
665  GetFakeHandler()->ReceiveMessage(
666      MCSMessage(kHeartbeatPingTag,
667                 heartbeat.PassAs<const google::protobuf::MessageLite>()));
668  PumpLoop();
669  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
670
671  // Rebuild the client. Nothing should be sent on login.
672  StoreCredentials();
673  BuildMCSClient();
674  InitializeClient();
675  LoginClient(std::vector<std::string>());
676  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
677}
678
679// If a message's TTL has expired by the time it reaches the front of the send
680// queue, it should be dropped.
681TEST_F(MCSClientTest, ExpiredTTLOnSend) {
682  BuildMCSClient();
683  InitializeClient();
684  LoginClient(std::vector<std::string>());
685  MCSMessage message(BuildDataMessage(
686      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
687
688  // Advance time to after the TTL.
689  clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2));
690  EXPECT_TRUE(sent_message_id().empty());
691  mcs_client()->SendMessage(message);
692
693  // No messages should be sent, but the callback should still be invoked.
694  EXPECT_EQ("X", sent_message_id());
695  EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status());
696  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
697}
698
699TEST_F(MCSClientTest, ExpiredTTLOnRestart) {
700  BuildMCSClient();
701  InitializeClient();
702  LoginClient(std::vector<std::string>());
703  GetFakeHandler()->set_fail_send(true);
704  MCSMessage message(BuildDataMessage(
705      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
706
707  // The initial (failed) send.
708  GetFakeHandler()->ExpectOutgoingMessage(message);
709  GetFakeHandler()->set_fail_send(false);
710  mcs_client()->SendMessage(message);
711  PumpLoop();
712  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
713
714  // Move the clock forward and rebuild the client, which should fail the
715  // message send on restart.
716  clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2));
717  StoreCredentials();
718  BuildMCSClient();
719  InitializeClient();
720  LoginClient(std::vector<std::string>());
721  PumpLoop();
722  EXPECT_EQ("X", sent_message_id());
723  EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status());
724  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
725}
726
727// Sending two messages with the same collapse key and same app id while
728// disconnected should only send the latter of the two on reconnection.
729TEST_F(MCSClientTest, CollapseKeysSameApp) {
730  BuildMCSClient();
731  InitializeClient();
732  MCSMessage message(BuildDataMessage(
733      "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
734  mcs_client()->SendMessage(message);
735
736  MCSMessage message2(BuildDataMessage(
737      "from", "app", "message id 2", 1, "1", kTTLValue, 1, 0, "token", 0));
738  mcs_client()->SendMessage(message2);
739
740  LoginClient(std::vector<std::string>());
741  GetFakeHandler()->ExpectOutgoingMessage(message2);
742  PumpLoop();
743}
744
745// Sending two messages with the same collapse key and different app id while
746// disconnected should not perform any collapsing.
747TEST_F(MCSClientTest, CollapseKeysDifferentApp) {
748  BuildMCSClient();
749  InitializeClient();
750  MCSMessage message(BuildDataMessage(
751      "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
752  mcs_client()->SendMessage(message);
753
754  MCSMessage message2(BuildDataMessage("from",
755                                       "app 2",
756                                       "message id 2",
757                                       1,
758                                       "2",
759                                       kTTLValue,
760                                       1,
761                                       0,
762                                       "token",
763                                       0));
764  mcs_client()->SendMessage(message2);
765
766  LoginClient(std::vector<std::string>());
767  GetFakeHandler()->ExpectOutgoingMessage(message);
768  GetFakeHandler()->ExpectOutgoingMessage(message2);
769  PumpLoop();
770}
771
772// Sending two messages with the same collapse key and app id, but different
773// user, while disconnected, should not perform any collapsing.
774TEST_F(MCSClientTest, CollapseKeysDifferentUser) {
775  BuildMCSClient();
776  InitializeClient();
777  MCSMessage message(BuildDataMessage(
778      "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
779  mcs_client()->SendMessage(message);
780
781  MCSMessage message2(BuildDataMessage("from",
782                                       "app",
783                                       "message id 2",
784                                       1,
785                                       "2",
786                                       kTTLValue,
787                                       1,
788                                       0,
789                                       "token",
790                                       1));
791  mcs_client()->SendMessage(message2);
792
793  LoginClient(std::vector<std::string>());
794  GetFakeHandler()->ExpectOutgoingMessage(message);
795  GetFakeHandler()->ExpectOutgoingMessage(message2);
796  PumpLoop();
797}
798
799} // namespace
800
801}  // namespace gcm
802