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 "google_apis/gcm/base/fake_encryptor.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/fake_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::FakeGCMStatsRecorder 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}
163
164void MCSClientTest::BuildMCSClient() {
165  gcm_store_.reset(new GCMStoreImpl(
166      temp_directory_.path(),
167      message_loop_.message_loop_proxy(),
168      make_scoped_ptr<Encryptor>(new FakeEncryptor)));
169  mcs_client_.reset(new TestMCSClient(&clock_,
170                                      &connection_factory_,
171                                      gcm_store_.get(),
172                                      &recorder_));
173}
174
175void MCSClientTest::InitializeClient() {
176  gcm_store_->Load(base::Bind(
177      &MCSClient::Initialize,
178      base::Unretained(mcs_client_.get()),
179      base::Bind(&MCSClientTest::ErrorCallback,
180                 base::Unretained(this)),
181      base::Bind(&MCSClientTest::MessageReceivedCallback,
182                 base::Unretained(this)),
183      base::Bind(&MCSClientTest::MessageSentCallback, base::Unretained(this))));
184  run_loop_->RunUntilIdle();
185  run_loop_.reset(new base::RunLoop());
186}
187
188void MCSClientTest::LoginClient(
189    const std::vector<std::string>& acknowledged_ids) {
190  scoped_ptr<mcs_proto::LoginRequest> login_request =
191      BuildLoginRequest(kAndroidId, kSecurityToken, "");
192  for (size_t i = 0; i < acknowledged_ids.size(); ++i)
193    login_request->add_received_persistent_id(acknowledged_ids[i]);
194  GetFakeHandler()->ExpectOutgoingMessage(
195      MCSMessage(kLoginRequestTag,
196                 login_request.PassAs<const google::protobuf::MessageLite>()));
197  mcs_client_->Login(kAndroidId, kSecurityToken);
198  run_loop_->Run();
199  run_loop_.reset(new base::RunLoop());
200}
201
202void MCSClientTest::StoreCredentials() {
203  gcm_store_->SetDeviceCredentials(
204      kAndroidId, kSecurityToken,
205      base::Bind(&MCSClientTest::SetDeviceCredentialsCallback,
206                 base::Unretained(this)));
207  run_loop_->Run();
208  run_loop_.reset(new base::RunLoop());
209}
210
211FakeConnectionHandler* MCSClientTest::GetFakeHandler() const {
212  return reinterpret_cast<FakeConnectionHandler*>(
213      connection_factory_.GetConnectionHandler());
214}
215
216void MCSClientTest::WaitForMCSEvent() {
217  run_loop_->Run();
218  run_loop_.reset(new base::RunLoop());
219}
220
221void MCSClientTest::PumpLoop() {
222  run_loop_->RunUntilIdle();
223  run_loop_.reset(new base::RunLoop());
224}
225
226void MCSClientTest::ErrorCallback() {
227  init_success_ = false;
228  DVLOG(1) << "Error callback invoked, killing loop.";
229  run_loop_->Quit();
230}
231
232void MCSClientTest::MessageReceivedCallback(const MCSMessage& message) {
233  received_message_.reset(new MCSMessage(message));
234  DVLOG(1) << "Message received callback invoked, killing loop.";
235  run_loop_->Quit();
236}
237
238void MCSClientTest::MessageSentCallback(int64 user_serial_number,
239                                        const std::string& app_id,
240                                        const std::string& message_id,
241                                        MCSClient::MessageSendStatus status) {
242  DVLOG(1) << "Message sent callback invoked, killing loop.";
243  sent_message_id_ = message_id;
244  message_send_status_ = status;
245  run_loop_->Quit();
246}
247
248void MCSClientTest::SetDeviceCredentialsCallback(bool success) {
249  ASSERT_TRUE(success);
250  run_loop_->Quit();
251}
252
253// Initialize a new client.
254TEST_F(MCSClientTest, InitializeNew) {
255  BuildMCSClient();
256  InitializeClient();
257  EXPECT_TRUE(init_success());
258}
259
260// Initialize a new client, shut it down, then restart the client. Should
261// reload the existing device credentials.
262TEST_F(MCSClientTest, InitializeExisting) {
263  BuildMCSClient();
264  InitializeClient();
265  LoginClient(std::vector<std::string>());
266
267  // Rebuild the client, to reload from the GCM store.
268  StoreCredentials();
269  BuildMCSClient();
270  InitializeClient();
271  EXPECT_TRUE(init_success());
272}
273
274// Log in successfully to the MCS endpoint.
275TEST_F(MCSClientTest, LoginSuccess) {
276  BuildMCSClient();
277  InitializeClient();
278  LoginClient(std::vector<std::string>());
279  EXPECT_TRUE(connection_factory()->IsEndpointReachable());
280  EXPECT_TRUE(init_success());
281  ASSERT_TRUE(received_message());
282  EXPECT_EQ(kLoginResponseTag, received_message()->tag());
283}
284
285// Encounter a server error during the login attempt. Should trigger a
286// reconnect.
287TEST_F(MCSClientTest, FailLogin) {
288  BuildMCSClient();
289  InitializeClient();
290  GetFakeHandler()->set_fail_login(true);
291  connection_factory()->set_delay_reconnect(true);
292  LoginClient(std::vector<std::string>());
293  EXPECT_FALSE(connection_factory()->IsEndpointReachable());
294  EXPECT_FALSE(init_success());
295  EXPECT_FALSE(received_message());
296  EXPECT_TRUE(connection_factory()->reconnect_pending());
297}
298
299// Send a message without RMQ support.
300TEST_F(MCSClientTest, SendMessageNoRMQ) {
301  BuildMCSClient();
302  InitializeClient();
303  LoginClient(std::vector<std::string>());
304  MCSMessage message(
305      BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
306  GetFakeHandler()->ExpectOutgoingMessage(message);
307  mcs_client()->SendMessage(message);
308  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
309}
310
311// Send a message without RMQ support while disconnected. Message send should
312// fail immediately, invoking callback.
313TEST_F(MCSClientTest, SendMessageNoRMQWhileDisconnected) {
314  BuildMCSClient();
315  InitializeClient();
316
317  EXPECT_TRUE(sent_message_id().empty());
318  MCSMessage message(
319      BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
320  mcs_client()->SendMessage(message);
321
322  // Message sent callback should be invoked, but no message should actually
323  // be sent.
324  EXPECT_EQ("X", sent_message_id());
325  EXPECT_EQ(MCSClient::NO_CONNECTION_ON_ZERO_TTL, message_send_status());
326  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
327}
328
329// Send a message with RMQ support.
330TEST_F(MCSClientTest, SendMessageRMQ) {
331  BuildMCSClient();
332  InitializeClient();
333  LoginClient(std::vector<std::string>());
334  MCSMessage message(BuildDataMessage(
335      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
336  GetFakeHandler()->ExpectOutgoingMessage(message);
337  mcs_client()->SendMessage(message);
338  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
339}
340
341// Send a message with RMQ support while disconnected. On reconnect, the message
342// should be resent.
343TEST_F(MCSClientTest, SendMessageRMQWhileDisconnected) {
344  BuildMCSClient();
345  InitializeClient();
346  LoginClient(std::vector<std::string>());
347  GetFakeHandler()->set_fail_send(true);
348  MCSMessage message(BuildDataMessage(
349      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
350
351  // The initial (failed) send.
352  GetFakeHandler()->ExpectOutgoingMessage(message);
353  // The login request.
354  GetFakeHandler()->ExpectOutgoingMessage(
355      MCSMessage(
356          kLoginRequestTag,
357          BuildLoginRequest(kAndroidId, kSecurityToken, "").
358              PassAs<const google::protobuf::MessageLite>()));
359  // The second (re)send.
360  MCSMessage message2(BuildDataMessage(
361      "from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
362  GetFakeHandler()->ExpectOutgoingMessage(message2);
363  mcs_client()->SendMessage(message);
364  PumpLoop();         // Wait for the queuing to happen.
365  EXPECT_EQ(MCSClient::QUEUED, message_send_status());
366  EXPECT_EQ("X", sent_message_id());
367  EXPECT_FALSE(GetFakeHandler()->AllOutgoingMessagesReceived());
368  GetFakeHandler()->set_fail_send(false);
369  clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1));
370  connection_factory()->Connect();
371  WaitForMCSEvent();  // Wait for the login to finish.
372  PumpLoop();         // Wait for the send to happen.
373
374  // Receive the ack.
375  scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
376  ack->set_last_stream_id_received(2);
377  GetFakeHandler()->ReceiveMessage(
378      MCSMessage(kIqStanzaTag,
379                 ack.PassAs<const google::protobuf::MessageLite>()));
380  WaitForMCSEvent();
381
382  EXPECT_EQ(MCSClient::SENT, message_send_status());
383  EXPECT_EQ("X", sent_message_id());
384  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
385}
386
387// Send a message with RMQ support without receiving an acknowledgement. On
388// restart the message should be resent.
389TEST_F(MCSClientTest, SendMessageRMQOnRestart) {
390  BuildMCSClient();
391  InitializeClient();
392  LoginClient(std::vector<std::string>());
393  GetFakeHandler()->set_fail_send(true);
394  MCSMessage message(BuildDataMessage(
395      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
396
397  // The initial (failed) send.
398  GetFakeHandler()->ExpectOutgoingMessage(message);
399  GetFakeHandler()->set_fail_send(false);
400  mcs_client()->SendMessage(message);
401  PumpLoop();
402  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
403
404  // Rebuild the client, which should resend the old message.
405  StoreCredentials();
406  BuildMCSClient();
407  InitializeClient();
408
409  clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1));
410  MCSMessage message2(BuildDataMessage(
411      "from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
412  LoginClient(std::vector<std::string>());
413  GetFakeHandler()->ExpectOutgoingMessage(message2);
414  PumpLoop();
415  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
416}
417
418// Send messages with RMQ support, followed by receiving a stream ack. On
419// restart nothing should be recent.
420TEST_F(MCSClientTest, SendMessageRMQWithStreamAck) {
421  BuildMCSClient();
422  InitializeClient();
423  LoginClient(std::vector<std::string>());
424
425  // Send some messages.
426  for (int i = 1; i <= kMessageBatchSize; ++i) {
427    MCSMessage message(BuildDataMessage("from",
428                                        "category",
429                                        "X",
430                                        1,
431                                        base::IntToString(i),
432                                        kTTLValue,
433                                        1,
434                                        0,
435                                        "",
436                                        0));
437    GetFakeHandler()->ExpectOutgoingMessage(message);
438    mcs_client()->SendMessage(message);
439    PumpLoop();
440  }
441  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
442
443  // Receive the ack.
444  scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
445  ack->set_last_stream_id_received(kMessageBatchSize + 1);
446  GetFakeHandler()->ReceiveMessage(
447      MCSMessage(kIqStanzaTag,
448                 ack.PassAs<const google::protobuf::MessageLite>()));
449  WaitForMCSEvent();
450
451  // Reconnect and ensure no messages are resent.
452  StoreCredentials();
453  BuildMCSClient();
454  InitializeClient();
455  LoginClient(std::vector<std::string>());
456  PumpLoop();
457}
458
459// Send messages with RMQ support. On restart, receive a SelectiveAck with
460// the login response. No messages should be resent.
461TEST_F(MCSClientTest, SendMessageRMQAckOnReconnect) {
462  BuildMCSClient();
463  InitializeClient();
464  LoginClient(std::vector<std::string>());
465
466  // Send some messages.
467  std::vector<std::string> id_list;
468  for (int i = 1; i <= kMessageBatchSize; ++i) {
469    id_list.push_back(base::IntToString(i));
470    MCSMessage message(BuildDataMessage("from",
471                                        "category",
472                                        id_list.back(),
473                                        1,
474                                        id_list.back(),
475                                        kTTLValue,
476                                        1,
477                                        0,
478                                        "",
479                                        0));
480    GetFakeHandler()->ExpectOutgoingMessage(message);
481    mcs_client()->SendMessage(message);
482    PumpLoop();
483  }
484  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
485
486  // Rebuild the client, and receive an acknowledgment for the messages as
487  // part of the login response.
488  StoreCredentials();
489  BuildMCSClient();
490  InitializeClient();
491  LoginClient(std::vector<std::string>());
492  scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(id_list));
493  GetFakeHandler()->ReceiveMessage(
494      MCSMessage(kIqStanzaTag,
495                 ack.PassAs<const google::protobuf::MessageLite>()));
496  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
497}
498
499// Send messages with RMQ support. On restart, receive a SelectiveAck with
500// the login response that only acks some messages. The unacked messages should
501// be resent.
502TEST_F(MCSClientTest, SendMessageRMQPartialAckOnReconnect) {
503  BuildMCSClient();
504  InitializeClient();
505  LoginClient(std::vector<std::string>());
506
507  // Send some messages.
508  std::vector<std::string> id_list;
509  for (int i = 1; i <= kMessageBatchSize; ++i) {
510    id_list.push_back(base::IntToString(i));
511    MCSMessage message(BuildDataMessage("from",
512                                        "category",
513                                        id_list.back(),
514                                        1,
515                                        id_list.back(),
516                                        kTTLValue,
517                                        1,
518                                        0,
519                                        "",
520                                        0));
521    GetFakeHandler()->ExpectOutgoingMessage(message);
522    mcs_client()->SendMessage(message);
523    PumpLoop();
524  }
525  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
526
527  // Rebuild the client, and receive an acknowledgment for the messages as
528  // part of the login response.
529  StoreCredentials();
530  BuildMCSClient();
531  InitializeClient();
532  LoginClient(std::vector<std::string>());
533
534  std::vector<std::string> acked_ids, remaining_ids;
535  acked_ids.insert(acked_ids.end(),
536                   id_list.begin(),
537                   id_list.begin() + kMessageBatchSize / 2);
538  remaining_ids.insert(remaining_ids.end(),
539                       id_list.begin() + kMessageBatchSize / 2,
540                       id_list.end());
541  for (int i = 1; i <= kMessageBatchSize / 2; ++i) {
542    MCSMessage message(BuildDataMessage("from",
543                                        "category",
544                                        remaining_ids[i - 1],
545                                        2,
546                                        remaining_ids[i - 1],
547                                        kTTLValue,
548                                        1,
549                                        0,
550                                        "",
551                                        0));
552    GetFakeHandler()->ExpectOutgoingMessage(message);
553  }
554  scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(acked_ids));
555  GetFakeHandler()->ReceiveMessage(
556      MCSMessage(kIqStanzaTag,
557                 ack.PassAs<const google::protobuf::MessageLite>()));
558  WaitForMCSEvent();
559  PumpLoop();
560  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
561}
562
563// Handle a selective ack that only acks some messages. The remaining unacked
564// messages should be resent. On restart, those same unacked messages should be
565// resent, and any pending acks for incoming messages should also be resent.
566TEST_F(MCSClientTest, SelectiveAckMidStream) {
567  BuildMCSClient();
568  InitializeClient();
569  LoginClient(std::vector<std::string>());
570
571  // Server stream id 2 ("s1").
572  // Acks client stream id 0 (login).
573  MCSMessage sMessage1(BuildDataMessage(
574      "from", "category", "X", 0, "s1", kTTLValue, 1, 0, "", 0));
575  GetFakeHandler()->ReceiveMessage(sMessage1);
576  WaitForMCSEvent();
577  PumpLoop();
578
579  // Client stream id 1 ("1").
580  // Acks server stream id 2 ("s1").
581  MCSMessage cMessage1(BuildDataMessage(
582      "from", "category", "Y", 2, "1", kTTLValue, 1, 0, "", 0));
583  GetFakeHandler()->ExpectOutgoingMessage(cMessage1);
584  mcs_client()->SendMessage(cMessage1);
585  PumpLoop();
586  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
587
588  // Server stream id 3 ("s2").
589  // Acks client stream id 1 ("1").
590  // Confirms ack of server stream id 2 ("s1").
591  MCSMessage sMessage2(BuildDataMessage(
592      "from", "category", "X", 1, "s2", kTTLValue, 1, 0, "", 0));
593  GetFakeHandler()->ReceiveMessage(sMessage2);
594  WaitForMCSEvent();
595  PumpLoop();
596
597  // Client Stream id 2 ("2").
598  // Acks server stream id 3 ("s2").
599  MCSMessage cMessage2(BuildDataMessage(
600      "from", "category", "Y", 3, "2", kTTLValue, 1, 0, "", 0));
601  GetFakeHandler()->ExpectOutgoingMessage(cMessage2);
602  mcs_client()->SendMessage(cMessage2);
603  PumpLoop();
604  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
605
606  // Simulate the last message being dropped by having the server selectively
607  // ack client message "1".
608  // Client message "2" should be resent, acking server stream id 4 (selective
609  // ack).
610  MCSMessage cMessage3(BuildDataMessage(
611      "from", "category", "Y", 4, "2", kTTLValue, 1, 0, "", 0));
612  GetFakeHandler()->ExpectOutgoingMessage(cMessage3);
613  std::vector<std::string> acked_ids(1, "1");
614  scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(acked_ids));
615  GetFakeHandler()->ReceiveMessage(
616      MCSMessage(kIqStanzaTag,
617                 ack.PassAs<const google::protobuf::MessageLite>()));
618  WaitForMCSEvent();
619  PumpLoop();
620  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
621
622  // Rebuild the client without any further acks from server. Note that this
623  // resets the stream ids.
624  // Sever message "s2" should be acked as part of login.
625  // Client message "2" should be resent.
626  StoreCredentials();
627  BuildMCSClient();
628  InitializeClient();
629
630  acked_ids[0] = "s2";
631  LoginClient(acked_ids);
632
633  MCSMessage cMessage4(BuildDataMessage(
634      "from", "category", "Y", 1, "2", kTTLValue, 1, 0, "", 0));
635  GetFakeHandler()->ExpectOutgoingMessage(cMessage4);
636  PumpLoop();
637  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
638}
639
640// Receive some messages. On restart, the login request should contain the
641// appropriate acknowledged ids.
642TEST_F(MCSClientTest, AckOnLogin) {
643  BuildMCSClient();
644  InitializeClient();
645  LoginClient(std::vector<std::string>());
646
647  // Receive some messages.
648  std::vector<std::string> id_list;
649  for (int i = 1; i <= kMessageBatchSize; ++i) {
650    id_list.push_back(base::IntToString(i));
651    MCSMessage message(BuildDataMessage(
652        "from", "category", "X", 1, id_list.back(), kTTLValue, 1, 0, "", 0));
653    GetFakeHandler()->ReceiveMessage(message);
654    WaitForMCSEvent();
655    PumpLoop();
656  }
657
658  // Restart the client.
659  StoreCredentials();
660  BuildMCSClient();
661  InitializeClient();
662  LoginClient(id_list);
663}
664
665// Receive some messages. On the next send, the outgoing message should contain
666// the appropriate last stream id received field to ack the received messages.
667TEST_F(MCSClientTest, AckOnSend) {
668  BuildMCSClient();
669  InitializeClient();
670  LoginClient(std::vector<std::string>());
671
672  // Receive some messages.
673  std::vector<std::string> id_list;
674  for (int i = 1; i <= kMessageBatchSize; ++i) {
675    id_list.push_back(base::IntToString(i));
676    MCSMessage message(BuildDataMessage("from",
677                                        "category",
678                                        id_list.back(),
679                                        1,
680                                        id_list.back(),
681                                        kTTLValue,
682                                        1,
683                                        0,
684                                        "",
685                                        0));
686    GetFakeHandler()->ReceiveMessage(message);
687    PumpLoop();
688  }
689
690  // Trigger a message send, which should acknowledge via stream ack.
691  MCSMessage message(BuildDataMessage("from",
692                                      "category",
693                                      "X",
694                                      kMessageBatchSize + 1,
695                                      "1",
696                                      kTTLValue,
697                                      1,
698                                      0,
699                                      "",
700                                      0));
701  GetFakeHandler()->ExpectOutgoingMessage(message);
702  mcs_client()->SendMessage(message);
703  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
704}
705
706// Receive the ack limit in messages, which should trigger an automatic
707// stream ack. Receive a heartbeat to confirm the ack.
708TEST_F(MCSClientTest, AckWhenLimitReachedWithHeartbeat) {
709  BuildMCSClient();
710  InitializeClient();
711  LoginClient(std::vector<std::string>());
712
713  // The stream ack.
714  scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
715  ack->set_last_stream_id_received(kAckLimitSize + 1);
716  GetFakeHandler()->ExpectOutgoingMessage(
717      MCSMessage(kIqStanzaTag,
718                 ack.PassAs<const google::protobuf::MessageLite>()));
719
720  // Receive some messages.
721  std::vector<std::string> id_list;
722  for (int i = 1; i <= kAckLimitSize; ++i) {
723    id_list.push_back(base::IntToString(i));
724    MCSMessage message(BuildDataMessage("from",
725                                        "category",
726                                        id_list.back(),
727                                        1,
728                                        id_list.back(),
729                                        kTTLValue,
730                                        1,
731                                        0,
732                                        "",
733                                        0));
734    GetFakeHandler()->ReceiveMessage(message);
735    WaitForMCSEvent();
736    PumpLoop();
737  }
738  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
739
740  // Receive a heartbeat confirming the ack (and receive the heartbeat ack).
741  scoped_ptr<mcs_proto::HeartbeatPing> heartbeat(
742      new mcs_proto::HeartbeatPing());
743  heartbeat->set_last_stream_id_received(2);
744
745  scoped_ptr<mcs_proto::HeartbeatAck> heartbeat_ack(
746      new mcs_proto::HeartbeatAck());
747  heartbeat_ack->set_last_stream_id_received(kAckLimitSize + 2);
748  GetFakeHandler()->ExpectOutgoingMessage(
749      MCSMessage(kHeartbeatAckTag,
750                 heartbeat_ack.PassAs<const google::protobuf::MessageLite>()));
751
752  GetFakeHandler()->ReceiveMessage(
753      MCSMessage(kHeartbeatPingTag,
754                 heartbeat.PassAs<const google::protobuf::MessageLite>()));
755  PumpLoop();
756  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
757
758  // Rebuild the client. Nothing should be sent on login.
759  StoreCredentials();
760  BuildMCSClient();
761  InitializeClient();
762  LoginClient(std::vector<std::string>());
763  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
764}
765
766// If a message's TTL has expired by the time it reaches the front of the send
767// queue, it should be dropped.
768TEST_F(MCSClientTest, ExpiredTTLOnSend) {
769  BuildMCSClient();
770  InitializeClient();
771  LoginClient(std::vector<std::string>());
772  MCSMessage message(BuildDataMessage(
773      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
774
775  // Advance time to after the TTL.
776  clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2));
777  EXPECT_TRUE(sent_message_id().empty());
778  mcs_client()->SendMessage(message);
779
780  // No messages should be sent, but the callback should still be invoked.
781  EXPECT_EQ("X", sent_message_id());
782  EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status());
783  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
784}
785
786TEST_F(MCSClientTest, ExpiredTTLOnRestart) {
787  BuildMCSClient();
788  InitializeClient();
789  LoginClient(std::vector<std::string>());
790  GetFakeHandler()->set_fail_send(true);
791  MCSMessage message(BuildDataMessage(
792      "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
793
794  // The initial (failed) send.
795  GetFakeHandler()->ExpectOutgoingMessage(message);
796  GetFakeHandler()->set_fail_send(false);
797  mcs_client()->SendMessage(message);
798  PumpLoop();
799  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
800
801  // Move the clock forward and rebuild the client, which should fail the
802  // message send on restart.
803  clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2));
804  StoreCredentials();
805  BuildMCSClient();
806  InitializeClient();
807  LoginClient(std::vector<std::string>());
808  PumpLoop();
809  EXPECT_EQ("X", sent_message_id());
810  EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status());
811  EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
812}
813
814// Sending two messages with the same collapse key and same app id while
815// disconnected should only send the latter of the two on reconnection.
816TEST_F(MCSClientTest, CollapseKeysSameApp) {
817  BuildMCSClient();
818  InitializeClient();
819  MCSMessage message(BuildDataMessage(
820      "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
821  mcs_client()->SendMessage(message);
822
823  MCSMessage message2(BuildDataMessage(
824      "from", "app", "message id 2", 1, "1", kTTLValue, 1, 0, "token", 0));
825  mcs_client()->SendMessage(message2);
826
827  LoginClient(std::vector<std::string>());
828  GetFakeHandler()->ExpectOutgoingMessage(message2);
829  PumpLoop();
830}
831
832// Sending two messages with the same collapse key and different app id while
833// disconnected should not perform any collapsing.
834TEST_F(MCSClientTest, CollapseKeysDifferentApp) {
835  BuildMCSClient();
836  InitializeClient();
837  MCSMessage message(BuildDataMessage(
838      "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
839  mcs_client()->SendMessage(message);
840
841  MCSMessage message2(BuildDataMessage("from",
842                                       "app 2",
843                                       "message id 2",
844                                       1,
845                                       "2",
846                                       kTTLValue,
847                                       1,
848                                       0,
849                                       "token",
850                                       0));
851  mcs_client()->SendMessage(message2);
852
853  LoginClient(std::vector<std::string>());
854  GetFakeHandler()->ExpectOutgoingMessage(message);
855  GetFakeHandler()->ExpectOutgoingMessage(message2);
856  PumpLoop();
857}
858
859// Sending two messages with the same collapse key and app id, but different
860// user, while disconnected, should not perform any collapsing.
861TEST_F(MCSClientTest, CollapseKeysDifferentUser) {
862  BuildMCSClient();
863  InitializeClient();
864  MCSMessage message(BuildDataMessage(
865      "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
866  mcs_client()->SendMessage(message);
867
868  MCSMessage message2(BuildDataMessage("from",
869                                       "app",
870                                       "message id 2",
871                                       1,
872                                       "2",
873                                       kTTLValue,
874                                       1,
875                                       0,
876                                       "token",
877                                       1));
878  mcs_client()->SendMessage(message2);
879
880  LoginClient(std::vector<std::string>());
881  GetFakeHandler()->ExpectOutgoingMessage(message);
882  GetFakeHandler()->ExpectOutgoingMessage(message2);
883  PumpLoop();
884}
885
886} // namespace
887
888}  // namespace gcm
889