• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
BuildDataMessage(const std::string & from,const std::string & category,int last_stream_id_received,const std::string persistent_id)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:
TestMCSClient(const base::FilePath & rmq_path,ConnectionFactory * connection_factory,scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)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 
GetNextPersistentId()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 
mcs_client() const74   TestMCSClient* mcs_client() const { return mcs_client_.get(); }
connection_factory()75   FakeConnectionFactory* connection_factory() {
76     return &connection_factory_;
77   }
init_success() const78   bool init_success() const { return init_success_; }
restored_android_id() const79   uint64 restored_android_id() const { return restored_android_id_; }
restored_security_token() const80   uint64 restored_security_token() const { return restored_security_token_; }
received_message() const81   MCSMessage* received_message() const { return received_message_.get(); }
sent_message_id() const82   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 
MCSClientTest()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 
~MCSClientTest()123 MCSClientTest::~MCSClientTest() {}
124 
BuildMCSClient()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 
InitializeClient()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 
LoginClient(const std::vector<std::string> & acknowledged_ids)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 
GetFakeHandler() const157 FakeConnectionHandler* MCSClientTest::GetFakeHandler() const {
158   return reinterpret_cast<FakeConnectionHandler*>(
159       connection_factory_.GetConnectionHandler());
160 }
161 
WaitForMCSEvent()162 void MCSClientTest::WaitForMCSEvent() {
163   run_loop_->Run();
164   run_loop_.reset(new base::RunLoop());
165 }
166 
PumpLoop()167 void MCSClientTest::PumpLoop() {
168   run_loop_->RunUntilIdle();
169   run_loop_.reset(new base::RunLoop());
170 }
171 
InitializationCallback(bool success,uint64 restored_android_id,uint64 restored_security_token)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 
MessageReceivedCallback(const MCSMessage & message)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 
MessageSentCallback(const std::string & message_id)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.
TEST_F(MCSClientTest,InitializeNew)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.
TEST_F(MCSClientTest,InitializeExisting)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.
TEST_F(MCSClientTest,LoginSuccess)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.
TEST_F(MCSClientTest,FailLogin)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.
TEST_F(MCSClientTest,SendMessageNoRMQ)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.
TEST_F(MCSClientTest,SendMessageRMQ)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.
TEST_F(MCSClientTest,SendMessageRMQWhileDisconnected)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.
TEST_F(MCSClientTest,SendMessageRMQOnRestart)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.
TEST_F(MCSClientTest,SendMessageRMQWithStreamAck)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.
TEST_F(MCSClientTest,SendMessageRMQAckOnReconnect)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.
TEST_F(MCSClientTest,SendMessageRMQPartialAckOnReconnect)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.
TEST_F(MCSClientTest,AckOnLogin)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.
TEST_F(MCSClientTest,AckOnSend)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.
TEST_F(MCSClientTest,AckWhenLimitReachedWithHeartbeat)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