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