Home | History | Annotate | Download | only in engine
      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