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 const std::string& token,
48 const uint64& user_id) {
49 mcs_proto::DataMessageStanza data_message;
50 data_message.set_id(message_id);
51 data_message.set_from(from);
52 data_message.set_category(category);
53 data_message.set_last_stream_id_received(last_stream_id_received);
54 if (!persistent_id.empty())
55 data_message.set_persistent_id(persistent_id);
56 data_message.set_ttl(ttl);
57 data_message.set_sent(sent);
58 data_message.set_queued(queued);
59 data_message.set_token(token);
60 data_message.set_device_user_id(user_id);
61 return MCSMessage(kDataMessageStanzaTag, data_message);
64 // MCSClient with overriden exposed persistent id logic.
65 class TestMCSClient : public MCSClient {
67 TestMCSClient(base::Clock* clock,
68 ConnectionFactory* connection_factory,
70 : MCSClient("", clock, connection_factory, gcm_store),
74 virtual std::string GetNextPersistentId() OVERRIDE {
75 return base::UintToString(++next_id_);
82 class MCSClientTest : public testing::Test {
85 virtual ~MCSClientTest();
87 void BuildMCSClient();
88 void InitializeClient();
89 void StoreCredentials();
90 void LoginClient(const std::vector<std::string>& acknowledged_ids);
92 base::SimpleTestClock* clock() { return &clock_; }
93 TestMCSClient* mcs_client() const { return mcs_client_.get(); }
94 FakeConnectionFactory* connection_factory() {
95 return &connection_factory_;
97 bool init_success() const { return init_success_; }
98 uint64 restored_android_id() const { return restored_android_id_; }
99 uint64 restored_security_token() const { return restored_security_token_; }
100 MCSMessage* received_message() const { return received_message_.get(); }
101 std::string sent_message_id() const { return sent_message_id_;}
102 MCSClient::MessageSendStatus message_send_status() const {
103 return message_send_status_;
106 void SetDeviceCredentialsCallback(bool success);
108 FakeConnectionHandler* GetFakeHandler() const;
110 void WaitForMCSEvent();
114 void ErrorCallback();
115 void MessageReceivedCallback(const MCSMessage& message);
116 void MessageSentCallback(int64 user_serial_number,
117 const std::string& app_id,
118 const std::string& message_id,
119 MCSClient::MessageSendStatus status);
121 base::SimpleTestClock clock_;
123 base::ScopedTempDir temp_directory_;
124 base::MessageLoop message_loop_;
125 scoped_ptr<base::RunLoop> run_loop_;
126 scoped_ptr<GCMStore> gcm_store_;
128 FakeConnectionFactory connection_factory_;
129 scoped_ptr<TestMCSClient> mcs_client_;
131 uint64 restored_android_id_;
132 uint64 restored_security_token_;
133 scoped_ptr<MCSMessage> received_message_;
134 std::string sent_message_id_;
135 MCSClient::MessageSendStatus message_send_status_;
138 MCSClientTest::MCSClientTest()
139 : run_loop_(new base::RunLoop()),
141 restored_android_id_(0),
142 restored_security_token_(0),
143 message_send_status_(MCSClient::SENT) {
144 EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
145 run_loop_.reset(new base::RunLoop());
147 // On OSX, prevent the Keychain permissions popup during unit tests.
148 #if defined(OS_MACOSX)
149 Encryptor::UseMockKeychain(true);
152 // Advance the clock to a non-zero time.
153 clock_.Advance(base::TimeDelta::FromSeconds(1));
156 MCSClientTest::~MCSClientTest() {}
158 void MCSClientTest::BuildMCSClient() {
159 gcm_store_.reset(new GCMStoreImpl(true,
160 temp_directory_.path(),
161 message_loop_.message_loop_proxy()));
162 mcs_client_.reset(new TestMCSClient(&clock_,
163 &connection_factory_,
167 void MCSClientTest::InitializeClient() {
168 gcm_store_->Load(base::Bind(
169 &MCSClient::Initialize,
170 base::Unretained(mcs_client_.get()),
171 base::Bind(&MCSClientTest::ErrorCallback,
172 base::Unretained(this)),
173 base::Bind(&MCSClientTest::MessageReceivedCallback,
174 base::Unretained(this)),
175 base::Bind(&MCSClientTest::MessageSentCallback, base::Unretained(this))));
176 run_loop_->RunUntilIdle();
177 run_loop_.reset(new base::RunLoop());
180 void MCSClientTest::LoginClient(
181 const std::vector<std::string>& acknowledged_ids) {
182 scoped_ptr<mcs_proto::LoginRequest> login_request =
183 BuildLoginRequest(kAndroidId, kSecurityToken, "");
184 for (size_t i = 0; i < acknowledged_ids.size(); ++i)
185 login_request->add_received_persistent_id(acknowledged_ids[i]);
186 GetFakeHandler()->ExpectOutgoingMessage(
187 MCSMessage(kLoginRequestTag,
188 login_request.PassAs<const google::protobuf::MessageLite>()));
189 mcs_client_->Login(kAndroidId, kSecurityToken);
191 run_loop_.reset(new base::RunLoop());
194 void MCSClientTest::StoreCredentials() {
195 gcm_store_->SetDeviceCredentials(
196 kAndroidId, kSecurityToken,
197 base::Bind(&MCSClientTest::SetDeviceCredentialsCallback,
198 base::Unretained(this)));
200 run_loop_.reset(new base::RunLoop());
203 FakeConnectionHandler* MCSClientTest::GetFakeHandler() const {
204 return reinterpret_cast<FakeConnectionHandler*>(
205 connection_factory_.GetConnectionHandler());
208 void MCSClientTest::WaitForMCSEvent() {
210 run_loop_.reset(new base::RunLoop());
213 void MCSClientTest::PumpLoop() {
214 run_loop_->RunUntilIdle();
215 run_loop_.reset(new base::RunLoop());
218 void MCSClientTest::ErrorCallback() {
219 init_success_ = false;
220 DVLOG(1) << "Error callback invoked, killing loop.";
224 void MCSClientTest::MessageReceivedCallback(const MCSMessage& message) {
225 received_message_.reset(new MCSMessage(message));
226 DVLOG(1) << "Message received callback invoked, killing loop.";
230 void MCSClientTest::MessageSentCallback(int64 user_serial_number,
231 const std::string& app_id,
232 const std::string& message_id,
233 MCSClient::MessageSendStatus status) {
234 DVLOG(1) << "Message sent callback invoked, killing loop.";
235 sent_message_id_ = message_id;
236 message_send_status_ = status;
240 void MCSClientTest::SetDeviceCredentialsCallback(bool success) {
241 ASSERT_TRUE(success);
245 // Initialize a new client.
246 TEST_F(MCSClientTest, InitializeNew) {
249 EXPECT_TRUE(init_success());
252 // Initialize a new client, shut it down, then restart the client. Should
253 // reload the existing device credentials.
254 TEST_F(MCSClientTest, InitializeExisting) {
257 LoginClient(std::vector<std::string>());
259 // Rebuild the client, to reload from the GCM store.
263 EXPECT_TRUE(init_success());
266 // Log in successfully to the MCS endpoint.
267 TEST_F(MCSClientTest, LoginSuccess) {
270 LoginClient(std::vector<std::string>());
271 EXPECT_TRUE(connection_factory()->IsEndpointReachable());
272 EXPECT_TRUE(init_success());
273 ASSERT_TRUE(received_message());
274 EXPECT_EQ(kLoginResponseTag, received_message()->tag());
277 // Encounter a server error during the login attempt. Should trigger a
279 TEST_F(MCSClientTest, FailLogin) {
282 GetFakeHandler()->set_fail_login(true);
283 connection_factory()->set_delay_reconnect(true);
284 LoginClient(std::vector<std::string>());
285 EXPECT_FALSE(connection_factory()->IsEndpointReachable());
286 EXPECT_FALSE(init_success());
287 EXPECT_FALSE(received_message());
288 EXPECT_TRUE(connection_factory()->reconnect_pending());
291 // Send a message without RMQ support.
292 TEST_F(MCSClientTest, SendMessageNoRMQ) {
295 LoginClient(std::vector<std::string>());
297 BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
298 GetFakeHandler()->ExpectOutgoingMessage(message);
299 mcs_client()->SendMessage(message);
300 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
303 // Send a message without RMQ support while disconnected. Message send should
304 // fail immediately, invoking callback.
305 TEST_F(MCSClientTest, SendMessageNoRMQWhileDisconnected) {
309 EXPECT_TRUE(sent_message_id().empty());
311 BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
312 mcs_client()->SendMessage(message);
314 // Message sent callback should be invoked, but no message should actually
316 EXPECT_EQ("X", sent_message_id());
317 EXPECT_EQ(MCSClient::NO_CONNECTION_ON_ZERO_TTL, message_send_status());
318 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
321 // Send a message with RMQ support.
322 TEST_F(MCSClientTest, SendMessageRMQ) {
325 LoginClient(std::vector<std::string>());
326 MCSMessage message(BuildDataMessage(
327 "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
328 GetFakeHandler()->ExpectOutgoingMessage(message);
329 mcs_client()->SendMessage(message);
330 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
333 // Send a message with RMQ support while disconnected. On reconnect, the message
335 TEST_F(MCSClientTest, SendMessageRMQWhileDisconnected) {
338 LoginClient(std::vector<std::string>());
339 GetFakeHandler()->set_fail_send(true);
340 MCSMessage message(BuildDataMessage(
341 "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
343 // The initial (failed) send.
344 GetFakeHandler()->ExpectOutgoingMessage(message);
345 // The login request.
346 GetFakeHandler()->ExpectOutgoingMessage(
349 BuildLoginRequest(kAndroidId, kSecurityToken, "").
350 PassAs<const google::protobuf::MessageLite>()));
351 // The second (re)send.
352 MCSMessage message2(BuildDataMessage(
353 "from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
354 GetFakeHandler()->ExpectOutgoingMessage(message2);
355 mcs_client()->SendMessage(message);
356 PumpLoop(); // Wait for the queuing to happen.
357 EXPECT_EQ(MCSClient::QUEUED, message_send_status());
358 EXPECT_FALSE(GetFakeHandler()->AllOutgoingMessagesReceived());
359 GetFakeHandler()->set_fail_send(false);
360 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1));
361 connection_factory()->Connect();
362 WaitForMCSEvent(); // Wait for the login to finish.
363 PumpLoop(); // Wait for the send to happen.
364 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
367 // Send a message with RMQ support without receiving an acknowledgement. On
368 // restart the message should be resent.
369 TEST_F(MCSClientTest, SendMessageRMQOnRestart) {
372 LoginClient(std::vector<std::string>());
373 GetFakeHandler()->set_fail_send(true);
374 MCSMessage message(BuildDataMessage(
375 "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
377 // The initial (failed) send.
378 GetFakeHandler()->ExpectOutgoingMessage(message);
379 GetFakeHandler()->set_fail_send(false);
380 mcs_client()->SendMessage(message);
382 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
384 // Rebuild the client, which should resend the old message.
389 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1));
390 MCSMessage message2(BuildDataMessage(
391 "from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
392 LoginClient(std::vector<std::string>());
393 GetFakeHandler()->ExpectOutgoingMessage(message2);
395 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
398 // Send messages with RMQ support, followed by receiving a stream ack. On
399 // restart nothing should be recent.
400 TEST_F(MCSClientTest, SendMessageRMQWithStreamAck) {
403 LoginClient(std::vector<std::string>());
405 // Send some messages.
406 for (int i = 1; i <= kMessageBatchSize; ++i) {
407 MCSMessage message(BuildDataMessage("from",
411 base::IntToString(i),
417 GetFakeHandler()->ExpectOutgoingMessage(message);
418 mcs_client()->SendMessage(message);
421 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
424 scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
425 ack->set_last_stream_id_received(kMessageBatchSize + 1);
426 GetFakeHandler()->ReceiveMessage(
427 MCSMessage(kIqStanzaTag,
428 ack.PassAs<const google::protobuf::MessageLite>()));
431 // Reconnect and ensure no messages are resent.
435 LoginClient(std::vector<std::string>());
439 // Send messages with RMQ support. On restart, receive a SelectiveAck with
440 // the login response. No messages should be resent.
441 TEST_F(MCSClientTest, SendMessageRMQAckOnReconnect) {
444 LoginClient(std::vector<std::string>());
446 // Send some messages.
447 std::vector<std::string> id_list;
448 for (int i = 1; i <= kMessageBatchSize; ++i) {
449 id_list.push_back(base::IntToString(i));
450 MCSMessage message(BuildDataMessage("from",
460 GetFakeHandler()->ExpectOutgoingMessage(message);
461 mcs_client()->SendMessage(message);
464 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
466 // Rebuild the client, and receive an acknowledgment for the messages as
467 // part of the login response.
471 LoginClient(std::vector<std::string>());
472 scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(id_list));
473 GetFakeHandler()->ReceiveMessage(
474 MCSMessage(kIqStanzaTag,
475 ack.PassAs<const google::protobuf::MessageLite>()));
476 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
479 // Send messages with RMQ support. On restart, receive a SelectiveAck with
480 // the login response that only acks some messages. The unacked messages should
482 TEST_F(MCSClientTest, SendMessageRMQPartialAckOnReconnect) {
485 LoginClient(std::vector<std::string>());
487 // Send some messages.
488 std::vector<std::string> id_list;
489 for (int i = 1; i <= kMessageBatchSize; ++i) {
490 id_list.push_back(base::IntToString(i));
491 MCSMessage message(BuildDataMessage("from",
501 GetFakeHandler()->ExpectOutgoingMessage(message);
502 mcs_client()->SendMessage(message);
505 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
507 // Rebuild the client, and receive an acknowledgment for the messages as
508 // part of the login response.
512 LoginClient(std::vector<std::string>());
514 std::vector<std::string> acked_ids, remaining_ids;
515 acked_ids.insert(acked_ids.end(),
517 id_list.begin() + kMessageBatchSize / 2);
518 remaining_ids.insert(remaining_ids.end(),
519 id_list.begin() + kMessageBatchSize / 2,
521 for (int i = 1; i <= kMessageBatchSize / 2; ++i) {
522 MCSMessage message(BuildDataMessage("from",
524 remaining_ids[i - 1],
526 remaining_ids[i - 1],
532 GetFakeHandler()->ExpectOutgoingMessage(message);
534 scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(acked_ids));
535 GetFakeHandler()->ReceiveMessage(
536 MCSMessage(kIqStanzaTag,
537 ack.PassAs<const google::protobuf::MessageLite>()));
540 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
543 // Receive some messages. On restart, the login request should contain the
544 // appropriate acknowledged ids.
545 TEST_F(MCSClientTest, AckOnLogin) {
548 LoginClient(std::vector<std::string>());
550 // Receive some messages.
551 std::vector<std::string> id_list;
552 for (int i = 1; i <= kMessageBatchSize; ++i) {
553 id_list.push_back(base::IntToString(i));
554 MCSMessage message(BuildDataMessage(
555 "from", "category", "X", 1, id_list.back(), kTTLValue, 1, 0, "", 0));
556 GetFakeHandler()->ReceiveMessage(message);
561 // Restart the client.
565 LoginClient(id_list);
568 // Receive some messages. On the next send, the outgoing message should contain
569 // the appropriate last stream id received field to ack the received messages.
570 TEST_F(MCSClientTest, AckOnSend) {
573 LoginClient(std::vector<std::string>());
575 // Receive some messages.
576 std::vector<std::string> id_list;
577 for (int i = 1; i <= kMessageBatchSize; ++i) {
578 id_list.push_back(base::IntToString(i));
579 MCSMessage message(BuildDataMessage("from",
589 GetFakeHandler()->ReceiveMessage(message);
593 // Trigger a message send, which should acknowledge via stream ack.
594 MCSMessage message(BuildDataMessage("from",
597 kMessageBatchSize + 1,
604 GetFakeHandler()->ExpectOutgoingMessage(message);
605 mcs_client()->SendMessage(message);
606 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
609 // Receive the ack limit in messages, which should trigger an automatic
610 // stream ack. Receive a heartbeat to confirm the ack.
611 TEST_F(MCSClientTest, AckWhenLimitReachedWithHeartbeat) {
614 LoginClient(std::vector<std::string>());
617 scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
618 ack->set_last_stream_id_received(kAckLimitSize + 1);
619 GetFakeHandler()->ExpectOutgoingMessage(
620 MCSMessage(kIqStanzaTag,
621 ack.PassAs<const google::protobuf::MessageLite>()));
623 // Receive some messages.
624 std::vector<std::string> id_list;
625 for (int i = 1; i <= kAckLimitSize; ++i) {
626 id_list.push_back(base::IntToString(i));
627 MCSMessage message(BuildDataMessage("from",
637 GetFakeHandler()->ReceiveMessage(message);
641 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
643 // Receive a heartbeat confirming the ack (and receive the heartbeat ack).
644 scoped_ptr<mcs_proto::HeartbeatPing> heartbeat(
645 new mcs_proto::HeartbeatPing());
646 heartbeat->set_last_stream_id_received(2);
648 scoped_ptr<mcs_proto::HeartbeatAck> heartbeat_ack(
649 new mcs_proto::HeartbeatAck());
650 heartbeat_ack->set_last_stream_id_received(kAckLimitSize + 2);
651 GetFakeHandler()->ExpectOutgoingMessage(
652 MCSMessage(kHeartbeatAckTag,
653 heartbeat_ack.PassAs<const google::protobuf::MessageLite>()));
655 GetFakeHandler()->ReceiveMessage(
656 MCSMessage(kHeartbeatPingTag,
657 heartbeat.PassAs<const google::protobuf::MessageLite>()));
659 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
661 // Rebuild the client. Nothing should be sent on login.
665 LoginClient(std::vector<std::string>());
666 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
669 // If a message's TTL has expired by the time it reaches the front of the send
670 // queue, it should be dropped.
671 TEST_F(MCSClientTest, ExpiredTTLOnSend) {
674 LoginClient(std::vector<std::string>());
675 MCSMessage message(BuildDataMessage(
676 "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
678 // Advance time to after the TTL.
679 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2));
680 EXPECT_TRUE(sent_message_id().empty());
681 mcs_client()->SendMessage(message);
683 // No messages should be sent, but the callback should still be invoked.
684 EXPECT_EQ("X", sent_message_id());
685 EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status());
686 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
689 TEST_F(MCSClientTest, ExpiredTTLOnRestart) {
692 LoginClient(std::vector<std::string>());
693 GetFakeHandler()->set_fail_send(true);
694 MCSMessage message(BuildDataMessage(
695 "from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
697 // The initial (failed) send.
698 GetFakeHandler()->ExpectOutgoingMessage(message);
699 GetFakeHandler()->set_fail_send(false);
700 mcs_client()->SendMessage(message);
702 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
704 // Move the clock forward and rebuild the client, which should fail the
705 // message send on restart.
706 clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2));
710 LoginClient(std::vector<std::string>());
712 EXPECT_EQ("X", sent_message_id());
713 EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status());
714 EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
717 // Sending two messages with the same collapse key and same app id while
718 // disconnected should only send the latter of the two on reconnection.
719 TEST_F(MCSClientTest, CollapseKeysSameApp) {
722 MCSMessage message(BuildDataMessage(
723 "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
724 mcs_client()->SendMessage(message);
726 MCSMessage message2(BuildDataMessage(
727 "from", "app", "message id 2", 1, "1", kTTLValue, 1, 0, "token", 0));
728 mcs_client()->SendMessage(message2);
730 LoginClient(std::vector<std::string>());
731 GetFakeHandler()->ExpectOutgoingMessage(message2);
735 // Sending two messages with the same collapse key and different app id while
736 // disconnected should not perform any collapsing.
737 TEST_F(MCSClientTest, CollapseKeysDifferentApp) {
740 MCSMessage message(BuildDataMessage(
741 "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
742 mcs_client()->SendMessage(message);
744 MCSMessage message2(BuildDataMessage("from",
754 mcs_client()->SendMessage(message2);
756 LoginClient(std::vector<std::string>());
757 GetFakeHandler()->ExpectOutgoingMessage(message);
758 GetFakeHandler()->ExpectOutgoingMessage(message2);
762 // Sending two messages with the same collapse key and app id, but different
763 // user, while disconnected, should not perform any collapsing.
764 TEST_F(MCSClientTest, CollapseKeysDifferentUser) {
767 MCSMessage message(BuildDataMessage(
768 "from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
769 mcs_client()->SendMessage(message);
771 MCSMessage message2(BuildDataMessage("from",
781 mcs_client()->SendMessage(message2);
783 LoginClient(std::vector<std::string>());
784 GetFakeHandler()->ExpectOutgoingMessage(message);
785 GetFakeHandler()->ExpectOutgoingMessage(message2);