This source file includes following definitions.
- BuildDataMessage
- next_id_
- GetNextPersistentId
- clock
- mcs_client
- connection_factory
- init_success
- restored_android_id
- restored_security_token
- received_message
- sent_message_id
- message_send_status
- message_send_status_
- BuildMCSClient
- InitializeClient
- LoginClient
- StoreCredentials
- GetFakeHandler
- WaitForMCSEvent
- PumpLoop
- ErrorCallback
- MessageReceivedCallback
- MessageSentCallback
- SetDeviceCredentialsCallback
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
#include "google_apis/gcm/engine/mcs_client.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/simple_test_clock.h"
#include "components/os_crypt/os_crypt.h"
#include "google_apis/gcm/base/mcs_util.h"
#include "google_apis/gcm/engine/fake_connection_factory.h"
#include "google_apis/gcm/engine/fake_connection_handler.h"
#include "google_apis/gcm/engine/gcm_store_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gcm {
namespace {
const uint64 kAndroidId = 54321;
const uint64 kSecurityToken = 12345;
const int kMessageBatchSize = 6;
const int kAckLimitSize = 10;
const int kTTLValue = 5 * 60;
MCSMessage BuildDataMessage(const std::string& from,
const std::string& category,
const std::string& message_id,
int last_stream_id_received,
const std::string& persistent_id,
int ttl,
uint64 sent,
int queued,
const std::string& token,
const uint64& user_id) {
mcs_proto::DataMessageStanza data_message;
data_message.set_id(message_id);
data_message.set_from(from);
data_message.set_category(category);
data_message.set_last_stream_id_received(last_stream_id_received);
if (!persistent_id.empty())
data_message.set_persistent_id(persistent_id);
data_message.set_ttl(ttl);
data_message.set_sent(sent);
data_message.set_queued(queued);
data_message.set_token(token);
data_message.set_device_user_id(user_id);
return MCSMessage(kDataMessageStanzaTag, data_message);
}
class TestMCSClient : public MCSClient {
public:
TestMCSClient(base::Clock* clock,
ConnectionFactory* connection_factory,
GCMStore* gcm_store)
: MCSClient("", clock, connection_factory, gcm_store),
next_id_(0) {
}
virtual std::string GetNextPersistentId() OVERRIDE {
return base::UintToString(++next_id_);
}
private:
uint32 next_id_;
};
class MCSClientTest : public testing::Test {
public:
MCSClientTest();
virtual ~MCSClientTest();
void BuildMCSClient();
void InitializeClient();
void StoreCredentials();
void LoginClient(const std::vector<std::string>& acknowledged_ids);
base::SimpleTestClock* clock() { return &clock_; }
TestMCSClient* mcs_client() const { return mcs_client_.get(); }
FakeConnectionFactory* connection_factory() {
return &connection_factory_;
}
bool init_success() const { return init_success_; }
uint64 restored_android_id() const { return restored_android_id_; }
uint64 restored_security_token() const { return restored_security_token_; }
MCSMessage* received_message() const { return received_message_.get(); }
std::string sent_message_id() const { return sent_message_id_;}
MCSClient::MessageSendStatus message_send_status() const {
return message_send_status_;
}
void SetDeviceCredentialsCallback(bool success);
FakeConnectionHandler* GetFakeHandler() const;
void WaitForMCSEvent();
void PumpLoop();
private:
void ErrorCallback();
void MessageReceivedCallback(const MCSMessage& message);
void MessageSentCallback(int64 user_serial_number,
const std::string& app_id,
const std::string& message_id,
MCSClient::MessageSendStatus status);
base::SimpleTestClock clock_;
base::ScopedTempDir temp_directory_;
base::MessageLoop message_loop_;
scoped_ptr<base::RunLoop> run_loop_;
scoped_ptr<GCMStore> gcm_store_;
FakeConnectionFactory connection_factory_;
scoped_ptr<TestMCSClient> mcs_client_;
bool init_success_;
uint64 restored_android_id_;
uint64 restored_security_token_;
scoped_ptr<MCSMessage> received_message_;
std::string sent_message_id_;
MCSClient::MessageSendStatus message_send_status_;
};
MCSClientTest::MCSClientTest()
: run_loop_(new base::RunLoop()),
init_success_(true),
restored_android_id_(0),
restored_security_token_(0),
message_send_status_(MCSClient::SENT) {
EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
run_loop_.reset(new base::RunLoop());
#if defined(OS_MACOSX)
OSCrypt::UseMockKeychain(true);
#endif
clock_.Advance(base::TimeDelta::FromSeconds(1));
}
MCSClientTest::~MCSClientTest() {}
void MCSClientTest::BuildMCSClient() {
gcm_store_.reset(new GCMStoreImpl(temp_directory_.path(),
message_loop_.message_loop_proxy()));
mcs_client_.reset(new TestMCSClient(&clock_,
&connection_factory_,
gcm_store_.get()));
}
void MCSClientTest::InitializeClient() {
gcm_store_->Load(base::Bind(
&MCSClient::Initialize,
base::Unretained(mcs_client_.get()),
base::Bind(&MCSClientTest::ErrorCallback,
base::Unretained(this)),
base::Bind(&MCSClientTest::MessageReceivedCallback,
base::Unretained(this)),
base::Bind(&MCSClientTest::MessageSentCallback, base::Unretained(this))));
run_loop_->RunUntilIdle();
run_loop_.reset(new base::RunLoop());
}
void MCSClientTest::LoginClient(
const std::vector<std::string>& acknowledged_ids) {
scoped_ptr<mcs_proto::LoginRequest> login_request =
BuildLoginRequest(kAndroidId, kSecurityToken, "");
for (size_t i = 0; i < acknowledged_ids.size(); ++i)
login_request->add_received_persistent_id(acknowledged_ids[i]);
GetFakeHandler()->ExpectOutgoingMessage(
MCSMessage(kLoginRequestTag,
login_request.PassAs<const google::protobuf::MessageLite>()));
mcs_client_->Login(kAndroidId, kSecurityToken);
run_loop_->Run();
run_loop_.reset(new base::RunLoop());
}
void MCSClientTest::StoreCredentials() {
gcm_store_->SetDeviceCredentials(
kAndroidId, kSecurityToken,
base::Bind(&MCSClientTest::SetDeviceCredentialsCallback,
base::Unretained(this)));
run_loop_->Run();
run_loop_.reset(new base::RunLoop());
}
FakeConnectionHandler* MCSClientTest::GetFakeHandler() const {
return reinterpret_cast<FakeConnectionHandler*>(
connection_factory_.GetConnectionHandler());
}
void MCSClientTest::WaitForMCSEvent() {
run_loop_->Run();
run_loop_.reset(new base::RunLoop());
}
void MCSClientTest::PumpLoop() {
run_loop_->RunUntilIdle();
run_loop_.reset(new base::RunLoop());
}
void MCSClientTest::ErrorCallback() {
init_success_ = false;
DVLOG(1) << "Error callback invoked, killing loop.";
run_loop_->Quit();
}
void MCSClientTest::MessageReceivedCallback(const MCSMessage& message) {
received_message_.reset(new MCSMessage(message));
DVLOG(1) << "Message received callback invoked, killing loop.";
run_loop_->Quit();
}
void MCSClientTest::MessageSentCallback(int64 user_serial_number,
const std::string& app_id,
const std::string& message_id,
MCSClient::MessageSendStatus status) {
DVLOG(1) << "Message sent callback invoked, killing loop.";
sent_message_id_ = message_id;
message_send_status_ = status;
run_loop_->Quit();
}
void MCSClientTest::SetDeviceCredentialsCallback(bool success) {
ASSERT_TRUE(success);
run_loop_->Quit();
}
TEST_F(MCSClientTest, InitializeNew) {
BuildMCSClient();
InitializeClient();
EXPECT_TRUE(init_success());
}
TEST_F(MCSClientTest, InitializeExisting) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
StoreCredentials();
BuildMCSClient();
InitializeClient();
EXPECT_TRUE(init_success());
}
TEST_F(MCSClientTest, LoginSuccess) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
EXPECT_TRUE(connection_factory()->IsEndpointReachable());
EXPECT_TRUE(init_success());
ASSERT_TRUE(received_message());
EXPECT_EQ(kLoginResponseTag, received_message()->tag());
}
TEST_F(MCSClientTest, FailLogin) {
BuildMCSClient();
InitializeClient();
GetFakeHandler()->set_fail_login(true);
connection_factory()->set_delay_reconnect(true);
LoginClient(std::vector<std::string>());
EXPECT_FALSE(connection_factory()->IsEndpointReachable());
EXPECT_FALSE(init_success());
EXPECT_FALSE(received_message());
EXPECT_TRUE(connection_factory()->reconnect_pending());
}
TEST_F(MCSClientTest, SendMessageNoRMQ) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
MCSMessage message(
BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, SendMessageNoRMQWhileDisconnected) {
BuildMCSClient();
InitializeClient();
EXPECT_TRUE(sent_message_id().empty());
MCSMessage message(
BuildDataMessage("from", "category", "X", 1, "", 0, 1, 0, "", 0));
mcs_client()->SendMessage(message);
EXPECT_EQ("X", sent_message_id());
EXPECT_EQ(MCSClient::NO_CONNECTION_ON_ZERO_TTL, message_send_status());
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, SendMessageRMQ) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
MCSMessage message(BuildDataMessage(
"from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, SendMessageRMQWhileDisconnected) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
GetFakeHandler()->set_fail_send(true);
MCSMessage message(BuildDataMessage(
"from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
GetFakeHandler()->ExpectOutgoingMessage(message);
GetFakeHandler()->ExpectOutgoingMessage(
MCSMessage(
kLoginRequestTag,
BuildLoginRequest(kAndroidId, kSecurityToken, "").
PassAs<const google::protobuf::MessageLite>()));
MCSMessage message2(BuildDataMessage(
"from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
GetFakeHandler()->ExpectOutgoingMessage(message2);
mcs_client()->SendMessage(message);
PumpLoop();
EXPECT_EQ(MCSClient::QUEUED, message_send_status());
EXPECT_FALSE(GetFakeHandler()->AllOutgoingMessagesReceived());
GetFakeHandler()->set_fail_send(false);
clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1));
connection_factory()->Connect();
WaitForMCSEvent();
PumpLoop();
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, SendMessageRMQOnRestart) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
GetFakeHandler()->set_fail_send(true);
MCSMessage message(BuildDataMessage(
"from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
GetFakeHandler()->ExpectOutgoingMessage(message);
GetFakeHandler()->set_fail_send(false);
mcs_client()->SendMessage(message);
PumpLoop();
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
StoreCredentials();
BuildMCSClient();
InitializeClient();
clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue - 1));
MCSMessage message2(BuildDataMessage(
"from", "category", "X", 1, "1", kTTLValue, 1, kTTLValue - 1, "", 0));
LoginClient(std::vector<std::string>());
GetFakeHandler()->ExpectOutgoingMessage(message2);
PumpLoop();
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, SendMessageRMQWithStreamAck) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
for (int i = 1; i <= kMessageBatchSize; ++i) {
MCSMessage message(BuildDataMessage("from",
"category",
"X",
1,
base::IntToString(i),
kTTLValue,
1,
0,
"",
0));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
PumpLoop();
}
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
ack->set_last_stream_id_received(kMessageBatchSize + 1);
GetFakeHandler()->ReceiveMessage(
MCSMessage(kIqStanzaTag,
ack.PassAs<const google::protobuf::MessageLite>()));
WaitForMCSEvent();
StoreCredentials();
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
PumpLoop();
}
TEST_F(MCSClientTest, SendMessageRMQAckOnReconnect) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
std::vector<std::string> id_list;
for (int i = 1; i <= kMessageBatchSize; ++i) {
id_list.push_back(base::IntToString(i));
MCSMessage message(BuildDataMessage("from",
"category",
id_list.back(),
1,
id_list.back(),
kTTLValue,
1,
0,
"",
0));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
PumpLoop();
}
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
StoreCredentials();
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(id_list));
GetFakeHandler()->ReceiveMessage(
MCSMessage(kIqStanzaTag,
ack.PassAs<const google::protobuf::MessageLite>()));
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, SendMessageRMQPartialAckOnReconnect) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
std::vector<std::string> id_list;
for (int i = 1; i <= kMessageBatchSize; ++i) {
id_list.push_back(base::IntToString(i));
MCSMessage message(BuildDataMessage("from",
"category",
id_list.back(),
1,
id_list.back(),
kTTLValue,
1,
0,
"",
0));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
PumpLoop();
}
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
StoreCredentials();
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
std::vector<std::string> acked_ids, remaining_ids;
acked_ids.insert(acked_ids.end(),
id_list.begin(),
id_list.begin() + kMessageBatchSize / 2);
remaining_ids.insert(remaining_ids.end(),
id_list.begin() + kMessageBatchSize / 2,
id_list.end());
for (int i = 1; i <= kMessageBatchSize / 2; ++i) {
MCSMessage message(BuildDataMessage("from",
"category",
remaining_ids[i - 1],
2,
remaining_ids[i - 1],
kTTLValue,
1,
0,
"",
0));
GetFakeHandler()->ExpectOutgoingMessage(message);
}
scoped_ptr<mcs_proto::IqStanza> ack(BuildSelectiveAck(acked_ids));
GetFakeHandler()->ReceiveMessage(
MCSMessage(kIqStanzaTag,
ack.PassAs<const google::protobuf::MessageLite>()));
WaitForMCSEvent();
PumpLoop();
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, AckOnLogin) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
std::vector<std::string> id_list;
for (int i = 1; i <= kMessageBatchSize; ++i) {
id_list.push_back(base::IntToString(i));
MCSMessage message(BuildDataMessage(
"from", "category", "X", 1, id_list.back(), kTTLValue, 1, 0, "", 0));
GetFakeHandler()->ReceiveMessage(message);
WaitForMCSEvent();
PumpLoop();
}
StoreCredentials();
BuildMCSClient();
InitializeClient();
LoginClient(id_list);
}
TEST_F(MCSClientTest, AckOnSend) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
std::vector<std::string> id_list;
for (int i = 1; i <= kMessageBatchSize; ++i) {
id_list.push_back(base::IntToString(i));
MCSMessage message(BuildDataMessage("from",
"category",
id_list.back(),
1,
id_list.back(),
kTTLValue,
1,
0,
"",
0));
GetFakeHandler()->ReceiveMessage(message);
PumpLoop();
}
MCSMessage message(BuildDataMessage("from",
"category",
"X",
kMessageBatchSize + 1,
"1",
kTTLValue,
1,
0,
"",
0));
GetFakeHandler()->ExpectOutgoingMessage(message);
mcs_client()->SendMessage(message);
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, AckWhenLimitReachedWithHeartbeat) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
scoped_ptr<mcs_proto::IqStanza> ack = BuildStreamAck();
ack->set_last_stream_id_received(kAckLimitSize + 1);
GetFakeHandler()->ExpectOutgoingMessage(
MCSMessage(kIqStanzaTag,
ack.PassAs<const google::protobuf::MessageLite>()));
std::vector<std::string> id_list;
for (int i = 1; i <= kAckLimitSize; ++i) {
id_list.push_back(base::IntToString(i));
MCSMessage message(BuildDataMessage("from",
"category",
id_list.back(),
1,
id_list.back(),
kTTLValue,
1,
0,
"",
0));
GetFakeHandler()->ReceiveMessage(message);
WaitForMCSEvent();
PumpLoop();
}
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
scoped_ptr<mcs_proto::HeartbeatPing> heartbeat(
new mcs_proto::HeartbeatPing());
heartbeat->set_last_stream_id_received(2);
scoped_ptr<mcs_proto::HeartbeatAck> heartbeat_ack(
new mcs_proto::HeartbeatAck());
heartbeat_ack->set_last_stream_id_received(kAckLimitSize + 2);
GetFakeHandler()->ExpectOutgoingMessage(
MCSMessage(kHeartbeatAckTag,
heartbeat_ack.PassAs<const google::protobuf::MessageLite>()));
GetFakeHandler()->ReceiveMessage(
MCSMessage(kHeartbeatPingTag,
heartbeat.PassAs<const google::protobuf::MessageLite>()));
PumpLoop();
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
StoreCredentials();
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, ExpiredTTLOnSend) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
MCSMessage message(BuildDataMessage(
"from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2));
EXPECT_TRUE(sent_message_id().empty());
mcs_client()->SendMessage(message);
EXPECT_EQ("X", sent_message_id());
EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status());
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, ExpiredTTLOnRestart) {
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
GetFakeHandler()->set_fail_send(true);
MCSMessage message(BuildDataMessage(
"from", "category", "X", 1, "1", kTTLValue, 1, 0, "", 0));
GetFakeHandler()->ExpectOutgoingMessage(message);
GetFakeHandler()->set_fail_send(false);
mcs_client()->SendMessage(message);
PumpLoop();
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
clock()->Advance(base::TimeDelta::FromSeconds(kTTLValue + 2));
StoreCredentials();
BuildMCSClient();
InitializeClient();
LoginClient(std::vector<std::string>());
PumpLoop();
EXPECT_EQ("X", sent_message_id());
EXPECT_EQ(MCSClient::TTL_EXCEEDED, message_send_status());
EXPECT_TRUE(GetFakeHandler()->AllOutgoingMessagesReceived());
}
TEST_F(MCSClientTest, CollapseKeysSameApp) {
BuildMCSClient();
InitializeClient();
MCSMessage message(BuildDataMessage(
"from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
mcs_client()->SendMessage(message);
MCSMessage message2(BuildDataMessage(
"from", "app", "message id 2", 1, "1", kTTLValue, 1, 0, "token", 0));
mcs_client()->SendMessage(message2);
LoginClient(std::vector<std::string>());
GetFakeHandler()->ExpectOutgoingMessage(message2);
PumpLoop();
}
TEST_F(MCSClientTest, CollapseKeysDifferentApp) {
BuildMCSClient();
InitializeClient();
MCSMessage message(BuildDataMessage(
"from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
mcs_client()->SendMessage(message);
MCSMessage message2(BuildDataMessage("from",
"app 2",
"message id 2",
1,
"2",
kTTLValue,
1,
0,
"token",
0));
mcs_client()->SendMessage(message2);
LoginClient(std::vector<std::string>());
GetFakeHandler()->ExpectOutgoingMessage(message);
GetFakeHandler()->ExpectOutgoingMessage(message2);
PumpLoop();
}
TEST_F(MCSClientTest, CollapseKeysDifferentUser) {
BuildMCSClient();
InitializeClient();
MCSMessage message(BuildDataMessage(
"from", "app", "message id 1", 1, "1", kTTLValue, 1, 0, "token", 0));
mcs_client()->SendMessage(message);
MCSMessage message2(BuildDataMessage("from",
"app",
"message id 2",
1,
"2",
kTTLValue,
1,
0,
"token",
1));
mcs_client()->SendMessage(message2);
LoginClient(std::vector<std::string>());
GetFakeHandler()->ExpectOutgoingMessage(message);
GetFakeHandler()->ExpectOutgoingMessage(message2);
PumpLoop();
}
}
}