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/files/scoped_temp_dir.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "base/run_loop.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "components/webdata/encryptor/encryptor.h"
     12 #include "google_apis/gcm/base/mcs_util.h"
     13 #include "google_apis/gcm/engine/fake_connection_factory.h"
     14 #include "google_apis/gcm/engine/fake_connection_handler.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 
     17 namespace gcm {
     18 
     19 namespace {
     20 
     21 const uint64 kAndroidId = 54321;
     22 const uint64 kSecurityToken = 12345;
     23 
     24 // Number of messages to send when testing batching.
     25 // Note: must be even for tests that split batches in half.
     26 const int kMessageBatchSize = 6;
     27 
     28 // The number of unacked messages the client will receive before sending a
     29 // stream ack.
     30 // TODO(zea): get this (and other constants) directly from the mcs client.
     31 const int kAckLimitSize = 10;
     32 
     33 // Helper for building arbitrary data messages.
     34 MCSMessage BuildDataMessage(const std::string& from,
     35                             const std::string& category,
     36                             int last_stream_id_received,
     37                             const std::string persistent_id) {
     38   mcs_proto::DataMessageStanza data_message;
     39   data_message.set_from(from);
     40   data_message.set_category(category);
     41   data_message.set_last_stream_id_received(last_stream_id_received);
     42   if (!persistent_id.empty())
     43     data_message.set_persistent_id(persistent_id);
     44   return MCSMessage(kDataMessageStanzaTag, data_message);
     45 }
     46 
     47 // MCSClient with overriden exposed persistent id logic.
     48 class TestMCSClient : public MCSClient {
     49  public:
     50   TestMCSClient(const base::FilePath& rmq_path,
     51                 ConnectionFactory* connection_factory,
     52                 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
     53     : MCSClient(rmq_path, connection_factory, blocking_task_runner),
     54       next_id_(0) {
     55   }
     56 
     57   virtual std::string GetNextPersistentId() OVERRIDE {
     58     return base::UintToString(++next_id_);
     59   }
     60 
     61  private:
     62   uint32 next_id_;
     63 };
     64 
     65 class MCSClientTest : public testing::Test {
     66  public:
     67   MCSClientTest();
     68   virtual ~MCSClientTest();
     69 
     70   void BuildMCSClient();
     71   void InitializeClient();
     72   void LoginClient(const std::vector<std::string>& acknowledged_ids);
     73 
     74   TestMCSClient* mcs_client() const { return mcs_client_.get(); }
     75   FakeConnectionFactory* connection_factory() {
     76     return &connection_factory_;
     77   }
     78   bool init_success() const { return init_success_; }
     79   uint64 restored_android_id() const { return restored_android_id_; }
     80   uint64 restored_security_token() const { return restored_security_token_; }
     81   MCSMessage* received_message() const { return received_message_.get(); }
     82   std::string sent_message_id() const { return sent_message_id_;}
     83 
     84   FakeConnectionHandler* GetFakeHandler() const;
     85 
     86   void WaitForMCSEvent();
     87   void PumpLoop();
     88 
     89  private:
     90   void InitializationCallback(bool success,
     91                               uint64 restored_android_id,
     92                               uint64 restored_security_token);
     93   void MessageReceivedCallback(const MCSMessage& message);
     94   void MessageSentCallback(const std::string& message_id);
     95 
     96   base::ScopedTempDir temp_directory_;
     97   base::MessageLoop message_loop_;
     98   scoped_ptr<base::RunLoop> run_loop_;
     99 
    100   FakeConnectionFactory connection_factory_;
    101   scoped_ptr<TestMCSClient> mcs_client_;
    102   bool init_success_;
    103   uint64 restored_android_id_;
    104   uint64 restored_security_token_;
    105   scoped_ptr<MCSMessage> received_message_;
    106   std::string sent_message_id_;
    107 };
    108 
    109 MCSClientTest::MCSClientTest()
    110     : run_loop_(new base::RunLoop()),
    111       init_success_(false),
    112       restored_android_id_(0),
    113       restored_security_token_(0) {
    114   EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
    115   run_loop_.reset(new base::RunLoop());
    116 
    117   // On OSX, prevent the Keychain permissions popup during unit tests.
    118 #if defined(OS_MACOSX)
    119     Encryptor::UseMockKeychain(true);
    120 #endif
    121 }
    122 
    123 MCSClientTest::~MCSClientTest() {}
    124 
    125 void MCSClientTest::BuildMCSClient() {
    126   mcs_client_.reset(
    127       new TestMCSClient(temp_directory_.path(),
    128                         &connection_factory_,
    129                         message_loop_.message_loop_proxy()));
    130 }
    131 
    132 void MCSClientTest::InitializeClient() {
    133   mcs_client_->Initialize(base::Bind(&MCSClientTest::InitializationCallback,
    134                                      base::Unretained(this)),
    135                           base::Bind(&MCSClientTest::MessageReceivedCallback,
    136                                      base::Unretained(this)),
    137                           base::Bind(&MCSClientTest::MessageSentCallback,
    138                                      base::Unretained(this)));
    139   run_loop_->Run();
    140   run_loop_.reset(new base::RunLoop());
    141 }
    142 
    143 void MCSClientTest::LoginClient(
    144     const std::vector<std::string>& acknowledged_ids) {
    145   scoped_ptr<mcs_proto::LoginRequest> login_request =
    146       BuildLoginRequest(kAndroidId, kSecurityToken);
    147   for (size_t i = 0; i < acknowledged_ids.size(); ++i)
    148     login_request->add_received_persistent_id(acknowledged_ids[i]);
    149   GetFakeHandler()->ExpectOutgoingMessage(
    150       MCSMessage(kLoginRequestTag,
    151                  login_request.PassAs<const google::protobuf::MessageLite>()));
    152   mcs_client_->Login(kAndroidId, kSecurityToken);
    153   run_loop_->Run();
    154   run_loop_.reset(new base::RunLoop());
    155 }
    156 
    157 FakeConnectionHandler* MCSClientTest::GetFakeHandler() const {
    158   return reinterpret_cast<FakeConnectionHandler*>(
    159       connection_factory_.GetConnectionHandler());
    160 }
    161 
    162 void MCSClientTest::WaitForMCSEvent() {
    163   run_loop_->Run();
    164   run_loop_.reset(new base::RunLoop());
    165 }
    166 
    167 void MCSClientTest::PumpLoop() {
    168   run_loop_->RunUntilIdle();
    169   run_loop_.reset(new base::RunLoop());
    170 }
    171 
    172 void MCSClientTest::InitializationCallback(bool success,
    173                                            uint64 restored_android_id,
    174                                            uint64 restored_security_token) {
    175   init_success_ = success;
    176   restored_android_id_ = restored_android_id;
    177   restored_security_token_ = restored_security_token;
    178   DVLOG(1) << "Initialization callback invoked, killing loop.";
    179   run_loop_->Quit();
    180 }
    181 
    182 void MCSClientTest::MessageReceivedCallback(const MCSMessage& message) {
    183   received_message_.reset(new MCSMessage(message));
    184   DVLOG(1) << "Message received callback invoked, killing loop.";
    185   run_loop_->Quit();
    186 }
    187 
    188 void MCSClientTest::MessageSentCallback(const std::string& message_id) {
    189   DVLOG(1) << "Message sent callback invoked, killing loop.";
    190   run_loop_->Quit();
    191 }
    192 
    193 // Initialize a new client.
    194 TEST_F(MCSClientTest, InitializeNew) {
    195   BuildMCSClient();
    196   InitializeClient();
    197   EXPECT_EQ(0U, restored_android_id());
    198   EXPECT_EQ(0U, restored_security_token());
    199   EXPECT_TRUE(init_success());
    200 }
    201 
    202 // Initialize a new client, shut it down, then restart the client. Should
    203 // reload the existing device credentials.
    204 TEST_F(MCSClientTest, InitializeExisting) {
    205   BuildMCSClient();
    206   InitializeClient();
    207   LoginClient(std::vector<std::string>());
    208 
    209   // Rebuild the client, to reload from the RMQ.
    210   BuildMCSClient();
    211   InitializeClient();
    212   EXPECT_EQ(kAndroidId, restored_android_id());
    213   EXPECT_EQ(kSecurityToken, restored_security_token());
    214   EXPECT_TRUE(init_success());
    215 }
    216 
    217 // Log in successfully to the MCS endpoint.
    218 TEST_F(MCSClientTest, LoginSuccess) {
    219   BuildMCSClient();
    220   InitializeClient();
    221   LoginClient(std::vector<std::string>());
    222   EXPECT_TRUE(connection_factory()->IsEndpointReachable());
    223   EXPECT_TRUE(init_success());
    224   ASSERT_TRUE(received_message());
    225   EXPECT_EQ(kLoginResponseTag, received_message()->tag());
    226 }
    227 
    228 // Encounter a server error during the login attempt.
    229 TEST_F(MCSClientTest, FailLogin) {
    230   BuildMCSClient();
    231   InitializeClient();
    232   GetFakeHandler()->set_fail_login(true);
    233   LoginClient(std::vector<std::string>());
    234   EXPECT_FALSE(connection_factory()->IsEndpointReachable());
    235   EXPECT_FALSE(init_success());
    236   EXPECT_FALSE(received_message());
    237 }
    238 
    239 // Send a message without RMQ support.
    240 TEST_F(MCSClientTest, SendMessageNoRMQ) {
    241   BuildMCSClient();
    242   InitializeClient();
    243   LoginClient(std::vector<std::string>());
    244   MCSMessage message(BuildDataMessage("from", "category", 1, ""));
    245   GetFakeHandler()->ExpectOutgoingMessage(message);
    246   mcs_client()->SendMessage(message, false);
    247   EXPECT_TRUE(GetFakeHandler()->
    248                   AllOutgoingMessagesReceived());
    249 }
    250 
    251 // Send a message with RMQ support.
    252 TEST_F(MCSClientTest, SendMessageRMQ) {
    253   BuildMCSClient();
    254   InitializeClient();
    255   LoginClient(std::vector<std::string>());
    256   MCSMessage message(BuildDataMessage("from", "category", 1, "1"));
    257   GetFakeHandler()->ExpectOutgoingMessage(message);
    258   mcs_client()->SendMessage(message, true);
    259   EXPECT_TRUE(GetFakeHandler()->
    260                   AllOutgoingMessagesReceived());
    261 }
    262 
    263 // Send a message with RMQ support while disconnected. On reconnect, the message
    264 // should be resent.
    265 TEST_F(MCSClientTest, SendMessageRMQWhileDisconnected) {
    266   BuildMCSClient();
    267   InitializeClient();
    268   LoginClient(std::vector<std::string>());
    269   GetFakeHandler()->set_fail_send(true);
    270   MCSMessage message(BuildDataMessage("from", "category", 1, "1"));
    271 
    272   // The initial (failed) send.
    273   GetFakeHandler()->ExpectOutgoingMessage(message);
    274   // The login request.
    275   GetFakeHandler()->ExpectOutgoingMessage(
    276       MCSMessage(kLoginRequestTag,
    277                  BuildLoginRequest(kAndroidId, kSecurityToken).
    278                      PassAs<const google::protobuf::MessageLite>()));
    279   // The second (re)send.
    280   GetFakeHandler()->ExpectOutgoingMessage(message);
    281   mcs_client()->SendMessage(message, true);
    282   EXPECT_FALSE(GetFakeHandler()->
    283                   AllOutgoingMessagesReceived());
    284   GetFakeHandler()->set_fail_send(false);
    285   connection_factory()->Connect();
    286   WaitForMCSEvent();  // Wait for the login to finish.
    287   PumpLoop();         // Wait for the send to happen.
    288   EXPECT_TRUE(GetFakeHandler()->
    289                   AllOutgoingMessagesReceived());
    290 }
    291 
    292 // Send a message with RMQ support without receiving an acknowledgement. On
    293 // restart the message should be resent.
    294 TEST_F(MCSClientTest, SendMessageRMQOnRestart) {
    295   BuildMCSClient();
    296   InitializeClient();
    297   LoginClient(std::vector<std::string>());
    298   GetFakeHandler()->set_fail_send(true);
    299   MCSMessage message(BuildDataMessage("from", "category", 1, "1"));
    300 
    301   // The initial (failed) send.
    302   GetFakeHandler()->ExpectOutgoingMessage(message);
    303   GetFakeHandler()->set_fail_send(false);
    304   mcs_client()->SendMessage(message, true);
    305   EXPECT_TRUE(GetFakeHandler()->
    306                   AllOutgoingMessagesReceived());
    307 
    308   // Rebuild the client, which should resend the old message.
    309   BuildMCSClient();
    310   InitializeClient();
    311   LoginClient(std::vector<std::string>());
    312   GetFakeHandler()->ExpectOutgoingMessage(message);
    313   PumpLoop();
    314   EXPECT_TRUE(GetFakeHandler()->
    315                   AllOutgoingMessagesReceived());
    316 }
    317 
    318 // Send messages with RMQ support, followed by receiving a stream ack. On
    319 // restart nothing should be recent.
    320 TEST_F(MCSClientTest, SendMessageRMQWithStreamAck) {
    321   BuildMCSClient();
    322   InitializeClient();
    323   LoginClient(std::vector<std::string>());
    324 
    325   // Send some messages.
    326   for (int i = 1; i <= kMessageBatchSize; ++i) {
    327     MCSMessage message(
    328         BuildDataMessage("from", "category", 1, base::IntToString(i)));
    329     GetFakeHandler()->ExpectOutgoingMessage(message);
    330     mcs_client()->SendMessage(message, true);
    331   }
    332   EXPECT_TRUE(GetFakeHandler()->
    333                   AllOutgoingMessagesReceived());
    334 
    335   // Receive the ack.
    336   scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
    337   ack->set_last_stream_id_received(kMessageBatchSize + 1);
    338   GetFakeHandler()->ReceiveMessage(
    339       MCSMessage(kIqStanzaTag,
    340                  ack.PassAs<const google::protobuf::MessageLite>()));
    341   WaitForMCSEvent();
    342 
    343   // Reconnect and ensure no messages are resent.
    344   BuildMCSClient();
    345   InitializeClient();
    346   LoginClient(std::vector<std::string>());
    347   PumpLoop();
    348 }
    349 
    350 // Send messages with RMQ support. On restart, receive a SelectiveAck with
    351 // the login response. No messages should be resent.
    352 TEST_F(MCSClientTest, SendMessageRMQAckOnReconnect) {
    353   BuildMCSClient();
    354   InitializeClient();
    355   LoginClient(std::vector<std::string>());
    356 
    357   // Send some messages.
    358   std::vector<std::string> id_list;
    359   for (int i = 1; i <= kMessageBatchSize; ++i) {
    360     id_list.push_back(base::IntToString(i));
    361     MCSMessage message(
    362         BuildDataMessage("from", "category", 1, id_list.back()));
    363     GetFakeHandler()->ExpectOutgoingMessage(message);
    364     mcs_client()->SendMessage(message, true);
    365   }
    366   EXPECT_TRUE(GetFakeHandler()->
    367                   AllOutgoingMessagesReceived());
    368 
    369   // Rebuild the client, and receive an acknowledgment for the messages as
    370   // part of the login response.
    371   BuildMCSClient();
    372   InitializeClient();
    373   LoginClient(std::vector<std::string>());
    374   scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(id_list));
    375   GetFakeHandler()->ReceiveMessage(
    376       MCSMessage(kIqStanzaTag,
    377                  ack.PassAs<const google::protobuf::MessageLite>()));
    378   WaitForMCSEvent();
    379   EXPECT_TRUE(GetFakeHandler()->
    380                   AllOutgoingMessagesReceived());
    381 }
    382 
    383 // Send messages with RMQ support. On restart, receive a SelectiveAck with
    384 // the login response that only acks some messages. The unacked messages should
    385 // be resent.
    386 TEST_F(MCSClientTest, SendMessageRMQPartialAckOnReconnect) {
    387   BuildMCSClient();
    388   InitializeClient();
    389   LoginClient(std::vector<std::string>());
    390 
    391   // Send some messages.
    392   std::vector<std::string> id_list;
    393   for (int i = 1; i <= kMessageBatchSize; ++i) {
    394     id_list.push_back(base::IntToString(i));
    395     MCSMessage message(
    396         BuildDataMessage("from", "category", 1, id_list.back()));
    397     GetFakeHandler()->ExpectOutgoingMessage(message);
    398     mcs_client()->SendMessage(message, true);
    399   }
    400   EXPECT_TRUE(GetFakeHandler()->
    401                   AllOutgoingMessagesReceived());
    402 
    403   // Rebuild the client, and receive an acknowledgment for the messages as
    404   // part of the login response.
    405   BuildMCSClient();
    406   InitializeClient();
    407   LoginClient(std::vector<std::string>());
    408 
    409   std::vector<std::string> acked_ids, remaining_ids;
    410   acked_ids.insert(acked_ids.end(),
    411                    id_list.begin(),
    412                    id_list.begin() + kMessageBatchSize / 2);
    413   remaining_ids.insert(remaining_ids.end(),
    414                        id_list.begin() + kMessageBatchSize / 2,
    415                        id_list.end());
    416   for (int i = 1; i <= kMessageBatchSize / 2; ++i) {
    417     MCSMessage message(
    418         BuildDataMessage("from",
    419                          "category",
    420                          2,
    421                          remaining_ids[i - 1]));
    422     GetFakeHandler()->ExpectOutgoingMessage(message);
    423   }
    424   scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(acked_ids));
    425   GetFakeHandler()->ReceiveMessage(
    426       MCSMessage(kIqStanzaTag,
    427                  ack.PassAs<const google::protobuf::MessageLite>()));
    428   WaitForMCSEvent();
    429   EXPECT_TRUE(GetFakeHandler()->
    430                   AllOutgoingMessagesReceived());
    431 }
    432 
    433 // Receive some messages. On restart, the login request should contain the
    434 // appropriate acknowledged ids.
    435 TEST_F(MCSClientTest, AckOnLogin) {
    436   BuildMCSClient();
    437   InitializeClient();
    438   LoginClient(std::vector<std::string>());
    439 
    440   // Receive some messages.
    441   std::vector<std::string> id_list;
    442   for (int i = 1; i <= kMessageBatchSize; ++i) {
    443     id_list.push_back(base::IntToString(i));
    444     MCSMessage message(
    445         BuildDataMessage("from", "category", i, id_list.back()));
    446     GetFakeHandler()->ReceiveMessage(message);
    447     WaitForMCSEvent();
    448     PumpLoop();
    449   }
    450 
    451   // Restart the client.
    452   BuildMCSClient();
    453   InitializeClient();
    454   LoginClient(id_list);
    455 }
    456 
    457 // Receive some messages. On the next send, the outgoing message should contain
    458 // the appropriate last stream id received field to ack the received messages.
    459 TEST_F(MCSClientTest, AckOnSend) {
    460   BuildMCSClient();
    461   InitializeClient();
    462   LoginClient(std::vector<std::string>());
    463 
    464   // Receive some messages.
    465   std::vector<std::string> id_list;
    466   for (int i = 1; i <= kMessageBatchSize; ++i) {
    467     id_list.push_back(base::IntToString(i));
    468     MCSMessage message(
    469         BuildDataMessage("from", "category", i, id_list.back()));
    470     GetFakeHandler()->ReceiveMessage(message);
    471     WaitForMCSEvent();
    472     PumpLoop();
    473   }
    474 
    475   // Trigger a message send, which should acknowledge via stream ack.
    476   MCSMessage message(
    477       BuildDataMessage("from", "category", kMessageBatchSize + 1, "1"));
    478   GetFakeHandler()->ExpectOutgoingMessage(message);
    479   mcs_client()->SendMessage(message, true);
    480   EXPECT_TRUE(GetFakeHandler()->
    481                   AllOutgoingMessagesReceived());
    482 }
    483 
    484 // Receive the ack limit in messages, which should trigger an automatic
    485 // stream ack. Receive a heartbeat to confirm the ack.
    486 TEST_F(MCSClientTest, AckWhenLimitReachedWithHeartbeat) {
    487   BuildMCSClient();
    488   InitializeClient();
    489   LoginClient(std::vector<std::string>());
    490 
    491   // The stream ack.
    492   scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
    493   ack->set_last_stream_id_received(kAckLimitSize + 1);
    494   GetFakeHandler()->ExpectOutgoingMessage(
    495       MCSMessage(kIqStanzaTag,
    496                  ack.PassAs<const google::protobuf::MessageLite>()));
    497 
    498   // Receive some messages.
    499   std::vector<std::string> id_list;
    500   for (int i = 1; i <= kAckLimitSize; ++i) {
    501     id_list.push_back(base::IntToString(i));
    502     MCSMessage message(
    503         BuildDataMessage("from", "category", i, id_list.back()));
    504     GetFakeHandler()->ReceiveMessage(message);
    505     WaitForMCSEvent();
    506     PumpLoop();
    507   }
    508   EXPECT_TRUE(GetFakeHandler()->
    509                   AllOutgoingMessagesReceived());
    510 
    511   // Receive a heartbeat confirming the ack (and receive the heartbeat ack).
    512   scoped_ptr<mcs_proto::HeartbeatPing> heartbeat(
    513       new mcs_proto::HeartbeatPing());
    514   heartbeat->set_last_stream_id_received(2);
    515 
    516   scoped_ptr<mcs_proto::HeartbeatAck> heartbeat_ack(
    517       new mcs_proto::HeartbeatAck());
    518   heartbeat_ack->set_last_stream_id_received(kAckLimitSize + 2);
    519   GetFakeHandler()->ExpectOutgoingMessage(
    520       MCSMessage(kHeartbeatAckTag,
    521                  heartbeat_ack.PassAs<const google::protobuf::MessageLite>()));
    522 
    523   GetFakeHandler()->ReceiveMessage(
    524       MCSMessage(kHeartbeatPingTag,
    525                  heartbeat.PassAs<const google::protobuf::MessageLite>()));
    526   WaitForMCSEvent();
    527   EXPECT_TRUE(GetFakeHandler()->
    528                   AllOutgoingMessagesReceived());
    529 
    530   // Rebuild the client. Nothing should be sent on login.
    531   BuildMCSClient();
    532   InitializeClient();
    533   LoginClient(std::vector<std::string>());
    534   EXPECT_TRUE(GetFakeHandler()->
    535                   AllOutgoingMessagesReceived());
    536 }
    537 
    538 } // namespace
    539 
    540 }  // namespace gcm
    541