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.
5 #include "google_apis/gcm/engine/mcs_client.h"
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 "base/test/simple_test_clock.h"
12 #include "components/webdata/encryptor/encryptor.h"
13 #include "google_apis/gcm/base/mcs_util.h"
14 #include "google_apis/gcm/engine/fake_connection_factory.h"
15 #include "google_apis/gcm/engine/fake_connection_handler.h"
16 #include "google_apis/gcm/engine/gcm_store_impl.h"
17 #include "testing/gtest/include/gtest/gtest.h"
23 const uint64 kAndroidId
= 54321;
24 const uint64 kSecurityToken
= 12345;
26 // Number of messages to send when testing batching.
27 // Note: must be even for tests that split batches in half.
28 const int kMessageBatchSize
= 6;
30 // The number of unacked messages the client will receive before sending a
32 // TODO(zea): get this (and other constants) directly from the mcs client.
33 const int kAckLimitSize
= 10;
35 // TTL value for reliable messages.
36 const int kTTLValue
= 5 * 60; // 5 minutes.
38 // Helper for building arbitrary data messages.
39 MCSMessage
BuildDataMessage(const std::string
& from
,
40 const std::string
& category
,
41 const std::string
& message_id
,
42 int last_stream_id_received
,
43 const std::string
& persistent_id
,
47 mcs_proto::DataMessageStanza data_message
;
48 data_message
.set_id(message_id
);
49 data_message
.set_from(from
);
50 data_message
.set_category(category
);
51 data_message
.set_last_stream_id_received(last_stream_id_received
);
52 if (!persistent_id
.empty())
53 data_message
.set_persistent_id(persistent_id
);
54 data_message
.set_ttl(ttl
);
55 data_message
.set_sent(sent
);
56 data_message
.set_queued(queued
);
57 return MCSMessage(kDataMessageStanzaTag
, data_message
);
60 // MCSClient with overriden exposed persistent id logic.
61 class TestMCSClient
: public MCSClient
{
63 TestMCSClient(base::Clock
* clock
,
64 ConnectionFactory
* connection_factory
,
66 : MCSClient(clock
, connection_factory
, gcm_store
),
70 virtual std::string
GetNextPersistentId() OVERRIDE
{
71 return base::UintToString(++next_id_
);
78 class MCSClientTest
: public testing::Test
{
81 virtual ~MCSClientTest();
83 void BuildMCSClient();
84 void InitializeClient();
85 void StoreCredentials();
86 void LoginClient(const std::vector
<std::string
>& acknowledged_ids
);
88 base::SimpleTestClock
* clock() { return &clock_
; }
89 TestMCSClient
* mcs_client() const { return mcs_client_
.get(); }
90 FakeConnectionFactory
* connection_factory() {
91 return &connection_factory_
;
93 bool init_success() const { return init_success_
; }
94 std::vector
<int64
> user_serial_numbers() const {
95 std::vector
<int64
> user_serial_numbers
;
96 user_serial_numbers
.push_back(1LL);
97 return user_serial_numbers
;
99 uint64
restored_android_id() const { return restored_android_id_
; }
100 uint64
restored_security_token() const { return restored_security_token_
; }
101 MCSMessage
* received_message() const { return received_message_
.get(); }
102 std::string
sent_message_id() const { return sent_message_id_
;}
103 MCSClient::MessageSendStatus
message_send_status() const {
104 return message_send_status_
;
107 void SetDeviceCredentialsCallback(bool success
);
109 FakeConnectionHandler
* GetFakeHandler() const;
111 void WaitForMCSEvent();
115 void ErrorCallback();
116 void MessageReceivedCallback(const MCSMessage
& message
);
117 void MessageSentCallback(int64 user_serial_number
,
118 const std::string
& app_id
,
119 const std::string
& message_id
,
120 MCSClient::MessageSendStatus status
);
122 base::SimpleTestClock clock_
;
124 base::ScopedTempDir temp_directory_
;
125 base::MessageLoop message_loop_
;
126 scoped_ptr
<base::RunLoop
> run_loop_
;
127 scoped_ptr
<GCMStore
> gcm_store_
;
129 FakeConnectionFactory connection_factory_
;
130 scoped_ptr
<TestMCSClient
> mcs_client_
;
132 uint64 restored_android_id_
;
133 uint64 restored_security_token_
;
134 scoped_ptr
<MCSMessage
> received_message_
;
135 std::string sent_message_id_
;
136 MCSClient::MessageSendStatus message_send_status_
;
139 MCSClientTest::MCSClientTest()
140 : run_loop_(new base::RunLoop()),
142 restored_android_id_(0),
143 restored_security_token_(0),
144 message_send_status_(MCSClient::SENT
) {
145 EXPECT_TRUE(temp_directory_
.CreateUniqueTempDir());
146 run_loop_
.reset(new base::RunLoop());
148 // On OSX, prevent the Keychain permissions popup during unit tests.
149 #if defined(OS_MACOSX)
150 Encryptor::UseMockKeychain(true);
153 // Advance the clock to a non-zero time.
154 clock_
.Advance(base::TimeDelta::FromSeconds(1));
157 MCSClientTest::~MCSClientTest() {}
159 void MCSClientTest::BuildMCSClient() {
160 gcm_store_
.reset(new GCMStoreImpl(true,
161 temp_directory_
.path(),
162 message_loop_
.message_loop_proxy()));
163 mcs_client_
.reset(new TestMCSClient(&clock_
,
164 &connection_factory_
,
168 void MCSClientTest::InitializeClient() {
169 gcm_store_
->Load(base::Bind(
170 &MCSClient::Initialize
,
171 base::Unretained(mcs_client_
.get()),
172 base::Bind(&MCSClientTest::ErrorCallback
,
173 base::Unretained(this)),
174 base::Bind(&MCSClientTest::MessageReceivedCallback
,
175 base::Unretained(this)),
176 base::Bind(&MCSClientTest::MessageSentCallback
, base::Unretained(this))));
177 run_loop_
->RunUntilIdle();
178 run_loop_
.reset(new base::RunLoop());
181 void MCSClientTest::LoginClient(
182 const std::vector
<std::string
>& acknowledged_ids
) {
183 scoped_ptr
<mcs_proto::LoginRequest
> login_request
=
184 BuildLoginRequest(kAndroidId
, kSecurityToken
, user_serial_numbers());
185 for (size_t i
= 0; i
< acknowledged_ids
.size(); ++i
)
186 login_request
->add_received_persistent_id(acknowledged_ids
[i
]);
187 GetFakeHandler()->ExpectOutgoingMessage(
188 MCSMessage(kLoginRequestTag
,
189 login_request
.PassAs
<const google::protobuf::MessageLite
>()));
190 mcs_client_
->Login(kAndroidId
, kSecurityToken
, user_serial_numbers());
192 run_loop_
.reset(new base::RunLoop());
195 void MCSClientTest::StoreCredentials() {
196 gcm_store_
->SetDeviceCredentials(
197 kAndroidId
, kSecurityToken
,
198 base::Bind(&MCSClientTest::SetDeviceCredentialsCallback
,
199 base::Unretained(this)));
201 run_loop_
.reset(new base::RunLoop());
204 FakeConnectionHandler
* MCSClientTest::GetFakeHandler() const {
205 return reinterpret_cast<FakeConnectionHandler
*>(
206 connection_factory_
.GetConnectionHandler());
209 void MCSClientTest::WaitForMCSEvent() {
211 run_loop_
.reset(new base::RunLoop());
214 void MCSClientTest::PumpLoop() {
215 run_loop_
->RunUntilIdle();
216 run_loop_
.reset(new base::RunLoop());
219 void MCSClientTest::ErrorCallback() {
220 init_success_
= false;
221 DVLOG(1) << "Error callback invoked, killing loop.";
225 void MCSClientTest::MessageReceivedCallback(const MCSMessage
& message
) {
226 received_message_
.reset(new MCSMessage(message
));
227 DVLOG(1) << "Message received callback invoked, killing loop.";
231 void MCSClientTest::MessageSentCallback(int64 user_serial_number
,
232 const std::string
& app_id
,
233 const std::string
& message_id
,
234 MCSClient::MessageSendStatus status
) {
235 DVLOG(1) << "Message sent callback invoked, killing loop.";
236 sent_message_id_
= message_id
;
237 message_send_status_
= status
;
241 void MCSClientTest::SetDeviceCredentialsCallback(bool success
) {
242 ASSERT_TRUE(success
);
246 // Initialize a new client.
247 TEST_F(MCSClientTest
, InitializeNew
) {
250 EXPECT_TRUE(init_success());
253 // Initialize a new client, shut it down, then restart the client. Should
254 // reload the existing device credentials.
255 TEST_F(MCSClientTest
, InitializeExisting
) {
258 LoginClient(std::vector
<std::string
>());
260 // Rebuild the client, to reload from the GCM store.
264 EXPECT_TRUE(init_success());
267 // Log in successfully to the MCS endpoint.
268 TEST_F(MCSClientTest
, LoginSuccess
) {
271 LoginClient(std::vector
<std::string
>());
272 EXPECT_TRUE(connection_factory()->IsEndpointReachable());
273 EXPECT_TRUE(init_success());
274 ASSERT_TRUE(received_message());
275 EXPECT_EQ(kLoginResponseTag
, received_message()->tag());
278 // Encounter a server error during the login attempt.
279 TEST_F(MCSClientTest
, FailLogin
) {
282 GetFakeHandler()->set_fail_login(true);
283 LoginClient(std::vector
<std::string
>());
284 EXPECT_FALSE(connection_factory()->IsEndpointReachable());
285 EXPECT_FALSE(init_success());
286 EXPECT_FALSE(received_message());
289 // Send a message without RMQ support.
290 TEST_F(MCSClientTest
, SendMessageNoRMQ
) {
293 LoginClient(std::vector
<std::string
>());
294 MCSMessage
message(BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0));
295 GetFakeHandler()->ExpectOutgoingMessage(message
);
296 mcs_client()->SendMessage(message
);
297 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
300 // Send a message without RMQ support while disconnected. Message send should
301 // fail immediately, invoking callback.
302 TEST_F(MCSClientTest
, SendMessageNoRMQWhileDisconnected
) {
306 EXPECT_TRUE(sent_message_id().empty());
307 MCSMessage
message(BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0));
308 mcs_client()->SendMessage(message
);
310 // Message sent callback should be invoked, but no message should actually
312 EXPECT_EQ("X", sent_message_id());
313 EXPECT_EQ(MCSClient::NO_CONNECTION_ON_ZERO_TTL
, message_send_status());
314 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
317 // Send a message with RMQ support.
318 TEST_F(MCSClientTest
, SendMessageRMQ
) {
321 LoginClient(std::vector
<std::string
>());
323 BuildDataMessage("from", "category", "X", 1, "1", kTTLValue
, 1, 0));
324 GetFakeHandler()->ExpectOutgoingMessage(message
);
325 mcs_client()->SendMessage(message
);
326 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
329 // Send a message with RMQ support while disconnected. On reconnect, the message
331 TEST_F(MCSClientTest
, SendMessageRMQWhileDisconnected
) {
334 LoginClient(std::vector
<std::string
>());
335 GetFakeHandler()->set_fail_send(true);
337 BuildDataMessage("from", "category", "X", 1, "1", kTTLValue
, 1, 0));
339 // The initial (failed) send.
340 GetFakeHandler()->ExpectOutgoingMessage(message
);
341 // The login request.
342 GetFakeHandler()->ExpectOutgoingMessage(
345 BuildLoginRequest(kAndroidId
, kSecurityToken
, user_serial_numbers()).
346 PassAs
<const google::protobuf::MessageLite
>()));
347 // The second (re)send.
348 MCSMessage
message2(BuildDataMessage("from",
356 GetFakeHandler()->ExpectOutgoingMessage(message2
);
357 mcs_client()->SendMessage(message
);
358 PumpLoop(); // Wait for the queuing to happen.
359 EXPECT_EQ(MCSClient::QUEUED
, message_send_status());
360 EXPECT_FALSE(GetFakeHandler()->AllOutgoingMessagesReceived());
361 GetFakeHandler()->set_fail_send(false);
362 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue
- 1));
363 connection_factory()->Connect();
364 WaitForMCSEvent(); // Wait for the login to finish.
365 PumpLoop(); // Wait for the send to happen.
366 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
369 // Send a message with RMQ support without receiving an acknowledgement. On
370 // restart the message should be resent.
371 TEST_F(MCSClientTest
, SendMessageRMQOnRestart
) {
374 LoginClient(std::vector
<std::string
>());
375 GetFakeHandler()->set_fail_send(true);
377 BuildDataMessage("from", "category", "X", 1, "1", kTTLValue
, 1, 0));
379 // The initial (failed) send.
380 GetFakeHandler()->ExpectOutgoingMessage(message
);
381 GetFakeHandler()->set_fail_send(false);
382 mcs_client()->SendMessage(message
);
384 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
386 // Rebuild the client, which should resend the old message.
391 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue
- 1));
392 MCSMessage
message2(BuildDataMessage("from",
400 LoginClient(std::vector
<std::string
>());
401 GetFakeHandler()->ExpectOutgoingMessage(message2
);
403 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
406 // Send messages with RMQ support, followed by receiving a stream ack. On
407 // restart nothing should be recent.
408 TEST_F(MCSClientTest
, SendMessageRMQWithStreamAck
) {
411 LoginClient(std::vector
<std::string
>());
413 // Send some messages.
414 for (int i
= 1; i
<= kMessageBatchSize
; ++i
) {
416 BuildDataMessage("from",
420 base::IntToString(i
),
424 GetFakeHandler()->ExpectOutgoingMessage(message
);
425 mcs_client()->SendMessage(message
);
428 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
431 scoped_ptr
<mcs_proto::IqStanza
> ack
= BuildStreamAck();
432 ack
->set_last_stream_id_received(kMessageBatchSize
+ 1);
433 GetFakeHandler()->ReceiveMessage(
434 MCSMessage(kIqStanzaTag
,
435 ack
.PassAs
<const google::protobuf::MessageLite
>()));
438 // Reconnect and ensure no messages are resent.
442 LoginClient(std::vector
<std::string
>());
446 // Send messages with RMQ support. On restart, receive a SelectiveAck with
447 // the login response. No messages should be resent.
448 TEST_F(MCSClientTest
, SendMessageRMQAckOnReconnect
) {
451 LoginClient(std::vector
<std::string
>());
453 // Send some messages.
454 std::vector
<std::string
> id_list
;
455 for (int i
= 1; i
<= kMessageBatchSize
; ++i
) {
456 id_list
.push_back(base::IntToString(i
));
458 BuildDataMessage("from",
466 GetFakeHandler()->ExpectOutgoingMessage(message
);
467 mcs_client()->SendMessage(message
);
470 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
472 // Rebuild the client, and receive an acknowledgment for the messages as
473 // part of the login response.
477 LoginClient(std::vector
<std::string
>());
478 scoped_ptr
<mcs_proto::IqStanza
> ack(BuildSelectiveAck(id_list
));
479 GetFakeHandler()->ReceiveMessage(
480 MCSMessage(kIqStanzaTag
,
481 ack
.PassAs
<const google::protobuf::MessageLite
>()));
482 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
485 // Send messages with RMQ support. On restart, receive a SelectiveAck with
486 // the login response that only acks some messages. The unacked messages should
488 TEST_F(MCSClientTest
, SendMessageRMQPartialAckOnReconnect
) {
491 LoginClient(std::vector
<std::string
>());
493 // Send some messages.
494 std::vector
<std::string
> id_list
;
495 for (int i
= 1; i
<= kMessageBatchSize
; ++i
) {
496 id_list
.push_back(base::IntToString(i
));
498 BuildDataMessage("from",
506 GetFakeHandler()->ExpectOutgoingMessage(message
);
507 mcs_client()->SendMessage(message
);
510 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
512 // Rebuild the client, and receive an acknowledgment for the messages as
513 // part of the login response.
517 LoginClient(std::vector
<std::string
>());
519 std::vector
<std::string
> acked_ids
, remaining_ids
;
520 acked_ids
.insert(acked_ids
.end(),
522 id_list
.begin() + kMessageBatchSize
/ 2);
523 remaining_ids
.insert(remaining_ids
.end(),
524 id_list
.begin() + kMessageBatchSize
/ 2,
526 for (int i
= 1; i
<= kMessageBatchSize
/ 2; ++i
) {
528 BuildDataMessage("from",
530 remaining_ids
[i
- 1],
532 remaining_ids
[i
- 1],
535 GetFakeHandler()->ExpectOutgoingMessage(message
);
537 scoped_ptr
<mcs_proto::IqStanza
> ack(BuildSelectiveAck(acked_ids
));
538 GetFakeHandler()->ReceiveMessage(
539 MCSMessage(kIqStanzaTag
,
540 ack
.PassAs
<const google::protobuf::MessageLite
>()));
543 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
546 // Receive some messages. On restart, the login request should contain the
547 // appropriate acknowledged ids.
548 TEST_F(MCSClientTest
, AckOnLogin
) {
551 LoginClient(std::vector
<std::string
>());
553 // Receive some messages.
554 std::vector
<std::string
> id_list
;
555 for (int i
= 1; i
<= kMessageBatchSize
; ++i
) {
556 id_list
.push_back(base::IntToString(i
));
558 BuildDataMessage("from",
566 GetFakeHandler()->ReceiveMessage(message
);
571 // Restart the client.
575 LoginClient(id_list
);
578 // Receive some messages. On the next send, the outgoing message should contain
579 // the appropriate last stream id received field to ack the received messages.
580 TEST_F(MCSClientTest
, AckOnSend
) {
583 LoginClient(std::vector
<std::string
>());
585 // Receive some messages.
586 std::vector
<std::string
> id_list
;
587 for (int i
= 1; i
<= kMessageBatchSize
; ++i
) {
588 id_list
.push_back(base::IntToString(i
));
590 BuildDataMessage("from",
598 GetFakeHandler()->ReceiveMessage(message
);
602 // Trigger a message send, which should acknowledge via stream ack.
604 BuildDataMessage("from",
607 kMessageBatchSize
+ 1,
611 GetFakeHandler()->ExpectOutgoingMessage(message
);
612 mcs_client()->SendMessage(message
);
613 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
616 // Receive the ack limit in messages, which should trigger an automatic
617 // stream ack. Receive a heartbeat to confirm the ack.
618 TEST_F(MCSClientTest
, AckWhenLimitReachedWithHeartbeat
) {
621 LoginClient(std::vector
<std::string
>());
624 scoped_ptr
<mcs_proto::IqStanza
> ack
= BuildStreamAck();
625 ack
->set_last_stream_id_received(kAckLimitSize
+ 1);
626 GetFakeHandler()->ExpectOutgoingMessage(
627 MCSMessage(kIqStanzaTag
,
628 ack
.PassAs
<const google::protobuf::MessageLite
>()));
630 // Receive some messages.
631 std::vector
<std::string
> id_list
;
632 for (int i
= 1; i
<= kAckLimitSize
; ++i
) {
633 id_list
.push_back(base::IntToString(i
));
635 BuildDataMessage("from",
643 GetFakeHandler()->ReceiveMessage(message
);
647 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
649 // Receive a heartbeat confirming the ack (and receive the heartbeat ack).
650 scoped_ptr
<mcs_proto::HeartbeatPing
> heartbeat(
651 new mcs_proto::HeartbeatPing());
652 heartbeat
->set_last_stream_id_received(2);
654 scoped_ptr
<mcs_proto::HeartbeatAck
> heartbeat_ack(
655 new mcs_proto::HeartbeatAck());
656 heartbeat_ack
->set_last_stream_id_received(kAckLimitSize
+ 2);
657 GetFakeHandler()->ExpectOutgoingMessage(
658 MCSMessage(kHeartbeatAckTag
,
659 heartbeat_ack
.PassAs
<const google::protobuf::MessageLite
>()));
661 GetFakeHandler()->ReceiveMessage(
662 MCSMessage(kHeartbeatPingTag
,
663 heartbeat
.PassAs
<const google::protobuf::MessageLite
>()));
665 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
667 // Rebuild the client. Nothing should be sent on login.
671 LoginClient(std::vector
<std::string
>());
672 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
675 // If a message's TTL has expired by the time it reaches the front of the send
676 // queue, it should be dropped.
677 TEST_F(MCSClientTest
, ExpiredTTLOnSend
) {
680 LoginClient(std::vector
<std::string
>());
682 BuildDataMessage("from", "category", "X", 1, "1", kTTLValue
, 1, 0));
684 // Advance time to after the TTL.
685 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue
+ 2));
686 EXPECT_TRUE(sent_message_id().empty());
687 mcs_client()->SendMessage(message
);
689 // No messages should be sent, but the callback should still be invoked.
690 EXPECT_EQ("X", sent_message_id());
691 EXPECT_EQ(MCSClient::TTL_EXCEEDED
, message_send_status());
692 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
695 TEST_F(MCSClientTest
, ExpiredTTLOnRestart
) {
698 LoginClient(std::vector
<std::string
>());
699 GetFakeHandler()->set_fail_send(true);
701 BuildDataMessage("from", "category", "X", 1, "1", kTTLValue
, 1, 0));
703 // The initial (failed) send.
704 GetFakeHandler()->ExpectOutgoingMessage(message
);
705 GetFakeHandler()->set_fail_send(false);
706 mcs_client()->SendMessage(message
);
708 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
710 // Move the clock forward and rebuild the client, which should fail the
711 // message send on restart.
712 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue
+ 2));
716 LoginClient(std::vector
<std::string
>());
718 EXPECT_EQ("X", sent_message_id());
719 EXPECT_EQ(MCSClient::TTL_EXCEEDED
, message_send_status());
720 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());