• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 namespace gcm {
22 
23 namespace {
24 
25 const uint64 kAndroidId = 54321;
26 const 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.
30 const 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.
35 const int kAckLimitSize = 10;
36 
37 // TTL value for reliable messages.
38 const int kTTLValue = 5 * 60;  // 5 minutes.
39 
40 // Helper for building arbitrary data messages.
BuildDataMessage(const std::string & from,const std::string & category,const std::string & message_id,int last_stream_id_received,const std::string & persistent_id,int ttl,uint64 sent,int queued,const std::string & token,const uint64 & user_id)41 MCSMessage 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.
67 class TestMCSClient : public MCSClient {
68  public:
TestMCSClient(base::Clock * clock,ConnectionFactory * connection_factory,GCMStore * gcm_store,gcm::GCMStatsRecorder * recorder)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 
GetNextPersistentId()77   virtual std::string GetNextPersistentId() OVERRIDE {
78     return base::UintToString(++next_id_);
79   }
80 
81  private:
82   uint32 next_id_;
83 };
84 
85 class 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 
clock()97   base::SimpleTestClock* clock() { return &clock_; }
mcs_client() const98   TestMCSClient* mcs_client() const { return mcs_client_.get(); }
connection_factory()99   FakeConnectionFactory* connection_factory() {
100     return &connection_factory_;
101   }
init_success() const102   bool init_success() const { return init_success_; }
restored_android_id() const103   uint64 restored_android_id() const { return restored_android_id_; }
restored_security_token() const104   uint64 restored_security_token() const { return restored_security_token_; }
received_message() const105   MCSMessage* received_message() const { return received_message_.get(); }
sent_message_id() const106   std::string sent_message_id() const { return sent_message_id_;}
message_send_status() const107   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 
MCSClientTest()145 MCSClientTest::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 
~MCSClientTest()158 MCSClientTest::~MCSClientTest() {}
159 
SetUp()160 void MCSClientTest::SetUp() {
161   testing::Test::SetUp();
162 }
163 
BuildMCSClient()164 void 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 
InitializeClient()175 void 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 
LoginClient(const std::vector<std::string> & acknowledged_ids)188 void 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 
StoreCredentials()202 void 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 
GetFakeHandler() const211 FakeConnectionHandler* MCSClientTest::GetFakeHandler() const {
212   return reinterpret_cast<FakeConnectionHandler*>(
213       connection_factory_.GetConnectionHandler());
214 }
215 
WaitForMCSEvent()216 void MCSClientTest::WaitForMCSEvent() {
217   run_loop_->Run();
218   run_loop_.reset(new base::RunLoop());
219 }
220 
PumpLoop()221 void MCSClientTest::PumpLoop() {
222   run_loop_->RunUntilIdle();
223   run_loop_.reset(new base::RunLoop());
224 }
225 
ErrorCallback()226 void MCSClientTest::ErrorCallback() {
227   init_success_ = false;
228   DVLOG(1) << "Error callback invoked, killing loop.";
229   run_loop_->Quit();
230 }
231 
MessageReceivedCallback(const MCSMessage & message)232 void 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 
MessageSentCallback(int64 user_serial_number,const std::string & app_id,const std::string & message_id,MCSClient::MessageSendStatus status)238 void 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 
SetDeviceCredentialsCallback(bool success)248 void MCSClientTest::SetDeviceCredentialsCallback(bool success) {
249   ASSERT_TRUE(success);
250   run_loop_->Quit();
251 }
252 
253 // Initialize a new client.
TEST_F(MCSClientTest,InitializeNew)254 TEST_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.
TEST_F(MCSClientTest,InitializeExisting)262 TEST_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.
TEST_F(MCSClientTest,LoginSuccess)275 TEST_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.
TEST_F(MCSClientTest,FailLogin)287 TEST_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.
TEST_F(MCSClientTest,SendMessageNoRMQ)300 TEST_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.
TEST_F(MCSClientTest,SendMessageNoRMQWhileDisconnected)313 TEST_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.
TEST_F(MCSClientTest,SendMessageRMQ)330 TEST_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.
TEST_F(MCSClientTest,SendMessageRMQWhileDisconnected)343 TEST_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.
TEST_F(MCSClientTest,SendMessageRMQOnRestart)389 TEST_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.
TEST_F(MCSClientTest,SendMessageRMQWithStreamAck)420 TEST_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.
TEST_F(MCSClientTest,SendMessageRMQAckOnReconnect)461 TEST_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.
TEST_F(MCSClientTest,SendMessageRMQPartialAckOnReconnect)502 TEST_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.
TEST_F(MCSClientTest,SelectiveAckMidStream)566 TEST_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.
TEST_F(MCSClientTest,AckOnLogin)642 TEST_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.
TEST_F(MCSClientTest,AckOnSend)667 TEST_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.
TEST_F(MCSClientTest,AckWhenLimitReachedWithHeartbeat)708 TEST_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.
TEST_F(MCSClientTest,ExpiredTTLOnSend)768 TEST_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 
TEST_F(MCSClientTest,ExpiredTTLOnRestart)786 TEST_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.
TEST_F(MCSClientTest,CollapseKeysSameApp)816 TEST_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.
TEST_F(MCSClientTest,CollapseKeysDifferentApp)834 TEST_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.
TEST_F(MCSClientTest,CollapseKeysDifferentUser)861 TEST_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