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_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. 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. 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. 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. 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. 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. 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. 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. 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. 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 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. 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. 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. 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