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