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. 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: 69 TestMCSClient(base::Clock* clock, 70 ConnectionFactory* connection_factory, 71 GCMStore* gcm_store, 72 gcm::GCMStatsRecorder* recorder) 73 : MCSClient("", clock, connection_factory, gcm_store, recorder), 74 next_id_(0) { 75 } 76 77 virtual std::string GetNextPersistentId() OVERRIDE { 78 return base::UintToString(++next_id_); 79 } 80 81 private: 82 uint32 next_id_; 83 }; 84 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 97 base::SimpleTestClock* clock() { return &clock_; } 98 TestMCSClient* mcs_client() const { return mcs_client_.get(); } 99 FakeConnectionFactory* connection_factory() { 100 return &connection_factory_; 101 } 102 bool init_success() const { return init_success_; } 103 uint64 restored_android_id() const { return restored_android_id_; } 104 uint64 restored_security_token() const { return restored_security_token_; } 105 MCSMessage* received_message() const { return received_message_.get(); } 106 std::string sent_message_id() const { return sent_message_id_;} 107 MCSClient::MessageSendStatus message_send_status() const { 108 return message_send_status_; 109 } 110 111 void SetDeviceCredentialsCallback(bool success); 112 113 FakeConnectionHandler* GetFakeHandler() const; 114 115 void WaitForMCSEvent(); 116 void PumpLoop(); 117 118 private: 119 void ErrorCallback(); 120 void MessageReceivedCallback(const MCSMessage& message); 121 void MessageSentCallback(int64 user_serial_number, 122 const std::string& app_id, 123 const std::string& message_id, 124 MCSClient::MessageSendStatus status); 125 126 base::SimpleTestClock clock_; 127 128 base::ScopedTempDir temp_directory_; 129 base::MessageLoop message_loop_; 130 scoped_ptr<base::RunLoop> run_loop_; 131 scoped_ptr<GCMStore> gcm_store_; 132 133 FakeConnectionFactory connection_factory_; 134 scoped_ptr<TestMCSClient> mcs_client_; 135 bool init_success_; 136 uint64 restored_android_id_; 137 uint64 restored_security_token_; 138 scoped_ptr<MCSMessage> received_message_; 139 std::string sent_message_id_; 140 MCSClient::MessageSendStatus message_send_status_; 141 142 gcm::FakeGCMStatsRecorder recorder_; 143 }; 144 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 158 MCSClientTest::~MCSClientTest() {} 159 160 void MCSClientTest::SetUp() { 161 testing::Test::SetUp(); 162 } 163 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 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 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 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 211 FakeConnectionHandler* MCSClientTest::GetFakeHandler() const { 212 return reinterpret_cast<FakeConnectionHandler*>( 213 connection_factory_.GetConnectionHandler()); 214 } 215 216 void MCSClientTest::WaitForMCSEvent() { 217 run_loop_->Run(); 218 run_loop_.reset(new base::RunLoop()); 219 } 220 221 void MCSClientTest::PumpLoop() { 222 run_loop_->RunUntilIdle(); 223 run_loop_.reset(new base::RunLoop()); 224 } 225 226 void MCSClientTest::ErrorCallback() { 227 init_success_ = false; 228 DVLOG(1) << "Error callback invoked, killing loop."; 229 run_loop_->Quit(); 230 } 231 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 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 248 void MCSClientTest::SetDeviceCredentialsCallback(bool success) { 249 ASSERT_TRUE(success); 250 run_loop_->Quit(); 251 } 252 253 // Initialize a new client. 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. 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. 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. 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. 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. 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. 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. 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_FALSE(GetFakeHandler()->AllOutgoingMessagesReceived()); 367 GetFakeHandler()->set_fail_send(false); 368 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1)); 369 connection_factory()->Connect(); 370 WaitForMCSEvent(); // Wait for the login to finish. 371 PumpLoop(); // Wait for the send to happen. 372 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 373 } 374 375 // Send a message with RMQ support without receiving an acknowledgement. On 376 // restart the message should be resent. 377 TEST_F(MCSClientTest, SendMessageRMQOnRestart) { 378 BuildMCSClient(); 379 InitializeClient(); 380 LoginClient(std::vector<std::string>()); 381 GetFakeHandler()->set_fail_send(true); 382 MCSMessage message(BuildDataMessage( 383 "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0)); 384 385 // The initial (failed) send. 386 GetFakeHandler()->ExpectOutgoingMessage(message); 387 GetFakeHandler()->set_fail_send(false); 388 mcs_client()->SendMessage(message); 389 PumpLoop(); 390 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 391 392 // Rebuild the client, which should resend the old message. 393 StoreCredentials(); 394 BuildMCSClient(); 395 InitializeClient(); 396 397 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1)); 398 MCSMessage message2(BuildDataMessage( 399 "from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0)); 400 LoginClient(std::vector<std::string>()); 401 GetFakeHandler()->ExpectOutgoingMessage(message2); 402 PumpLoop(); 403 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 404 } 405 406 // Send messages with RMQ support, followed by receiving a stream ack. On 407 // restart nothing should be recent. 408 TEST_F(MCSClientTest, SendMessageRMQWithStreamAck) { 409 BuildMCSClient(); 410 InitializeClient(); 411 LoginClient(std::vector<std::string>()); 412 413 // Send some messages. 414 for (int i = 1; i <= kMessageBatchSize; ++i) { 415 MCSMessage message(BuildDataMessage("from", 416 "category", 417 "X", 418 1, 419 base::IntToString(i), 420 kTTLValue, 421 1, 422 0, 423 "", 424 0)); 425 GetFakeHandler()->ExpectOutgoingMessage(message); 426 mcs_client()->SendMessage(message); 427 PumpLoop(); 428 } 429 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 430 431 // Receive the ack. 432 scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck(); 433 ack->set_last_stream_id_received(kMessageBatchSize + 1); 434 GetFakeHandler()->ReceiveMessage( 435 MCSMessage(kIqStanzaTag, 436 ack.PassAs<const google::protobuf::MessageLite>())); 437 WaitForMCSEvent(); 438 439 // Reconnect and ensure no messages are resent. 440 StoreCredentials(); 441 BuildMCSClient(); 442 InitializeClient(); 443 LoginClient(std::vector<std::string>()); 444 PumpLoop(); 445 } 446 447 // Send messages with RMQ support. On restart, receive a SelectiveAck with 448 // the login response. No messages should be resent. 449 TEST_F(MCSClientTest, SendMessageRMQAckOnReconnect) { 450 BuildMCSClient(); 451 InitializeClient(); 452 LoginClient(std::vector<std::string>()); 453 454 // Send some messages. 455 std::vector<std::string> id_list; 456 for (int i = 1; i <= kMessageBatchSize; ++i) { 457 id_list.push_back(base::IntToString(i)); 458 MCSMessage message(BuildDataMessage("from", 459 "category", 460 id_list.back(), 461 1, 462 id_list.back(), 463 kTTLValue, 464 1, 465 0, 466 "", 467 0)); 468 GetFakeHandler()->ExpectOutgoingMessage(message); 469 mcs_client()->SendMessage(message); 470 PumpLoop(); 471 } 472 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 473 474 // Rebuild the client, and receive an acknowledgment for the messages as 475 // part of the login response. 476 StoreCredentials(); 477 BuildMCSClient(); 478 InitializeClient(); 479 LoginClient(std::vector<std::string>()); 480 scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(id_list)); 481 GetFakeHandler()->ReceiveMessage( 482 MCSMessage(kIqStanzaTag, 483 ack.PassAs<const google::protobuf::MessageLite>())); 484 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 485 } 486 487 // Send messages with RMQ support. On restart, receive a SelectiveAck with 488 // the login response that only acks some messages. The unacked messages should 489 // be resent. 490 TEST_F(MCSClientTest, SendMessageRMQPartialAckOnReconnect) { 491 BuildMCSClient(); 492 InitializeClient(); 493 LoginClient(std::vector<std::string>()); 494 495 // Send some messages. 496 std::vector<std::string> id_list; 497 for (int i = 1; i <= kMessageBatchSize; ++i) { 498 id_list.push_back(base::IntToString(i)); 499 MCSMessage message(BuildDataMessage("from", 500 "category", 501 id_list.back(), 502 1, 503 id_list.back(), 504 kTTLValue, 505 1, 506 0, 507 "", 508 0)); 509 GetFakeHandler()->ExpectOutgoingMessage(message); 510 mcs_client()->SendMessage(message); 511 PumpLoop(); 512 } 513 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 514 515 // Rebuild the client, and receive an acknowledgment for the messages as 516 // part of the login response. 517 StoreCredentials(); 518 BuildMCSClient(); 519 InitializeClient(); 520 LoginClient(std::vector<std::string>()); 521 522 std::vector<std::string> acked_ids, remaining_ids; 523 acked_ids.insert(acked_ids.end(), 524 id_list.begin(), 525 id_list.begin() + kMessageBatchSize / 2); 526 remaining_ids.insert(remaining_ids.end(), 527 id_list.begin() + kMessageBatchSize / 2, 528 id_list.end()); 529 for (int i = 1; i <= kMessageBatchSize / 2; ++i) { 530 MCSMessage message(BuildDataMessage("from", 531 "category", 532 remaining_ids[i - 1], 533 2, 534 remaining_ids[i - 1], 535 kTTLValue, 536 1, 537 0, 538 "", 539 0)); 540 GetFakeHandler()->ExpectOutgoingMessage(message); 541 } 542 scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(acked_ids)); 543 GetFakeHandler()->ReceiveMessage( 544 MCSMessage(kIqStanzaTag, 545 ack.PassAs<const google::protobuf::MessageLite>())); 546 WaitForMCSEvent(); 547 PumpLoop(); 548 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 549 } 550 551 // Receive some messages. On restart, the login request should contain the 552 // appropriate acknowledged ids. 553 TEST_F(MCSClientTest, AckOnLogin) { 554 BuildMCSClient(); 555 InitializeClient(); 556 LoginClient(std::vector<std::string>()); 557 558 // Receive some messages. 559 std::vector<std::string> id_list; 560 for (int i = 1; i <= kMessageBatchSize; ++i) { 561 id_list.push_back(base::IntToString(i)); 562 MCSMessage message(BuildDataMessage( 563 "from", "category", "X", 1, id_list.back(), kTTLValue, 1, 0, "", 0)); 564 GetFakeHandler()->ReceiveMessage(message); 565 WaitForMCSEvent(); 566 PumpLoop(); 567 } 568 569 // Restart the client. 570 StoreCredentials(); 571 BuildMCSClient(); 572 InitializeClient(); 573 LoginClient(id_list); 574 } 575 576 // Receive some messages. On the next send, the outgoing message should contain 577 // the appropriate last stream id received field to ack the received messages. 578 TEST_F(MCSClientTest, AckOnSend) { 579 BuildMCSClient(); 580 InitializeClient(); 581 LoginClient(std::vector<std::string>()); 582 583 // Receive some messages. 584 std::vector<std::string> id_list; 585 for (int i = 1; i <= kMessageBatchSize; ++i) { 586 id_list.push_back(base::IntToString(i)); 587 MCSMessage message(BuildDataMessage("from", 588 "category", 589 id_list.back(), 590 1, 591 id_list.back(), 592 kTTLValue, 593 1, 594 0, 595 "", 596 0)); 597 GetFakeHandler()->ReceiveMessage(message); 598 PumpLoop(); 599 } 600 601 // Trigger a message send, which should acknowledge via stream ack. 602 MCSMessage message(BuildDataMessage("from", 603 "category", 604 "X", 605 kMessageBatchSize + 1, 606 "1", 607 kTTLValue, 608 1, 609 0, 610 "", 611 0)); 612 GetFakeHandler()->ExpectOutgoingMessage(message); 613 mcs_client()->SendMessage(message); 614 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 615 } 616 617 // Receive the ack limit in messages, which should trigger an automatic 618 // stream ack. Receive a heartbeat to confirm the ack. 619 TEST_F(MCSClientTest, AckWhenLimitReachedWithHeartbeat) { 620 BuildMCSClient(); 621 InitializeClient(); 622 LoginClient(std::vector<std::string>()); 623 624 // The stream ack. 625 scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck(); 626 ack->set_last_stream_id_received(kAckLimitSize + 1); 627 GetFakeHandler()->ExpectOutgoingMessage( 628 MCSMessage(kIqStanzaTag, 629 ack.PassAs<const google::protobuf::MessageLite>())); 630 631 // Receive some messages. 632 std::vector<std::string> id_list; 633 for (int i = 1; i <= kAckLimitSize; ++i) { 634 id_list.push_back(base::IntToString(i)); 635 MCSMessage message(BuildDataMessage("from", 636 "category", 637 id_list.back(), 638 1, 639 id_list.back(), 640 kTTLValue, 641 1, 642 0, 643 "", 644 0)); 645 GetFakeHandler()->ReceiveMessage(message); 646 WaitForMCSEvent(); 647 PumpLoop(); 648 } 649 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 650 651 // Receive a heartbeat confirming the ack (and receive the heartbeat ack). 652 scoped_ptr<mcs_proto::HeartbeatPing> heartbeat( 653 new mcs_proto::HeartbeatPing()); 654 heartbeat->set_last_stream_id_received(2); 655 656 scoped_ptr<mcs_proto::HeartbeatAck> heartbeat_ack( 657 new mcs_proto::HeartbeatAck()); 658 heartbeat_ack->set_last_stream_id_received(kAckLimitSize + 2); 659 GetFakeHandler()->ExpectOutgoingMessage( 660 MCSMessage(kHeartbeatAckTag, 661 heartbeat_ack.PassAs<const google::protobuf::MessageLite>())); 662 663 GetFakeHandler()->ReceiveMessage( 664 MCSMessage(kHeartbeatPingTag, 665 heartbeat.PassAs<const google::protobuf::MessageLite>())); 666 PumpLoop(); 667 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 668 669 // Rebuild the client. Nothing should be sent on login. 670 StoreCredentials(); 671 BuildMCSClient(); 672 InitializeClient(); 673 LoginClient(std::vector<std::string>()); 674 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 675 } 676 677 // If a message's TTL has expired by the time it reaches the front of the send 678 // queue, it should be dropped. 679 TEST_F(MCSClientTest, ExpiredTTLOnSend) { 680 BuildMCSClient(); 681 InitializeClient(); 682 LoginClient(std::vector<std::string>()); 683 MCSMessage message(BuildDataMessage( 684 "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0)); 685 686 // Advance time to after the TTL. 687 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2)); 688 EXPECT_TRUE(sent_message_id().empty()); 689 mcs_client()->SendMessage(message); 690 691 // No messages should be sent, but the callback should still be invoked. 692 EXPECT_EQ("X", sent_message_id()); 693 EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status()); 694 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 695 } 696 697 TEST_F(MCSClientTest, ExpiredTTLOnRestart) { 698 BuildMCSClient(); 699 InitializeClient(); 700 LoginClient(std::vector<std::string>()); 701 GetFakeHandler()->set_fail_send(true); 702 MCSMessage message(BuildDataMessage( 703 "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0)); 704 705 // The initial (failed) send. 706 GetFakeHandler()->ExpectOutgoingMessage(message); 707 GetFakeHandler()->set_fail_send(false); 708 mcs_client()->SendMessage(message); 709 PumpLoop(); 710 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 711 712 // Move the clock forward and rebuild the client, which should fail the 713 // message send on restart. 714 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2)); 715 StoreCredentials(); 716 BuildMCSClient(); 717 InitializeClient(); 718 LoginClient(std::vector<std::string>()); 719 PumpLoop(); 720 EXPECT_EQ("X", sent_message_id()); 721 EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status()); 722 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived()); 723 } 724 725 // Sending two messages with the same collapse key and same app id while 726 // disconnected should only send the latter of the two on reconnection. 727 TEST_F(MCSClientTest, CollapseKeysSameApp) { 728 BuildMCSClient(); 729 InitializeClient(); 730 MCSMessage message(BuildDataMessage( 731 "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0)); 732 mcs_client()->SendMessage(message); 733 734 MCSMessage message2(BuildDataMessage( 735 "from", "app", "message id 2", 1, "1", kTTLValue, 1, 0, "token", 0)); 736 mcs_client()->SendMessage(message2); 737 738 LoginClient(std::vector<std::string>()); 739 GetFakeHandler()->ExpectOutgoingMessage(message2); 740 PumpLoop(); 741 } 742 743 // Sending two messages with the same collapse key and different app id while 744 // disconnected should not perform any collapsing. 745 TEST_F(MCSClientTest, CollapseKeysDifferentApp) { 746 BuildMCSClient(); 747 InitializeClient(); 748 MCSMessage message(BuildDataMessage( 749 "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0)); 750 mcs_client()->SendMessage(message); 751 752 MCSMessage message2(BuildDataMessage("from", 753 "app 2", 754 "message id 2", 755 1, 756 "2", 757 kTTLValue, 758 1, 759 0, 760 "token", 761 0)); 762 mcs_client()->SendMessage(message2); 763 764 LoginClient(std::vector<std::string>()); 765 GetFakeHandler()->ExpectOutgoingMessage(message); 766 GetFakeHandler()->ExpectOutgoingMessage(message2); 767 PumpLoop(); 768 } 769 770 // Sending two messages with the same collapse key and app id, but different 771 // user, while disconnected, should not perform any collapsing. 772 TEST_F(MCSClientTest, CollapseKeysDifferentUser) { 773 BuildMCSClient(); 774 InitializeClient(); 775 MCSMessage message(BuildDataMessage( 776 "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0)); 777 mcs_client()->SendMessage(message); 778 779 MCSMessage message2(BuildDataMessage("from", 780 "app", 781 "message id 2", 782 1, 783 "2", 784 kTTLValue, 785 1, 786 0, 787 "token", 788 1)); 789 mcs_client()->SendMessage(message2); 790 791 LoginClient(std::vector<std::string>()); 792 GetFakeHandler()->ExpectOutgoingMessage(message); 793 GetFakeHandler()->ExpectOutgoingMessage(message2); 794 PumpLoop(); 795 } 796 797 } // namespace 798 799 } // namespace gcm 800