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