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