summaryrefslogtreecommitdiff
path: root/chromium/components/gcm_driver/gcm_account_mapper_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/gcm_driver/gcm_account_mapper_unittest.cc')
-rw-r--r--chromium/components/gcm_driver/gcm_account_mapper_unittest.cc955
1 files changed, 955 insertions, 0 deletions
diff --git a/chromium/components/gcm_driver/gcm_account_mapper_unittest.cc b/chromium/components/gcm_driver/gcm_account_mapper_unittest.cc
new file mode 100644
index 00000000000..286d7e41915
--- /dev/null
+++ b/chromium/components/gcm_driver/gcm_account_mapper_unittest.cc
@@ -0,0 +1,955 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/gcm_driver/gcm_account_mapper.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/test/simple_test_clock.h"
+#include "base/time/time.h"
+#include "components/gcm_driver/fake_gcm_driver.h"
+#include "google_apis/gcm/engine/account_mapping.h"
+#include "google_apis/gcm/engine/gcm_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gcm {
+
+namespace {
+
+const char kGCMAccountMapperSenderId[] = "745476177629";
+const char kGCMAccountMapperSendTo[] = "google.com";
+const char kRegistrationId[] = "reg_id";
+const char kEmbeddedAppIdKey[] = "gcmb";
+const char kTestAppId[] = "test_app_id";
+const char kTestDataKey[] = "data_key";
+const char kTestDataValue[] = "data_value";
+const char kTestCollapseKey[] = "test_collapse_key";
+const char kTestSenderId[] = "test_sender_id";
+
+AccountMapping MakeAccountMapping(const CoreAccountId& account_id,
+ AccountMapping::MappingStatus status,
+ const base::Time& status_change_timestamp,
+ const std::string& last_message_id) {
+ AccountMapping account_mapping;
+ account_mapping.account_id = account_id;
+ account_mapping.email = account_id.ToString() + "@gmail.com";
+ // account_mapping.access_token intentionally left empty.
+ account_mapping.status = status;
+ account_mapping.status_change_timestamp = status_change_timestamp;
+ account_mapping.last_message_id = last_message_id;
+ return account_mapping;
+}
+
+GCMClient::AccountTokenInfo MakeAccountTokenInfo(
+ const CoreAccountId& account_id) {
+ GCMClient::AccountTokenInfo account_token;
+ account_token.account_id = account_id;
+ account_token.email = account_id.ToString() + "@gmail.com";
+ account_token.access_token = account_id.ToString() + "_token";
+ return account_token;
+}
+
+void VerifyMappings(const GCMAccountMapper::AccountMappings& expected_mappings,
+ const GCMAccountMapper::AccountMappings& actual_mappings,
+ const std::string& verification_info) {
+ EXPECT_EQ(expected_mappings.size(), actual_mappings.size())
+ << "Verification Info: " << verification_info;
+ auto expected_iter = expected_mappings.begin();
+ auto actual_iter = actual_mappings.begin();
+ for (; expected_iter != expected_mappings.end() &&
+ actual_iter != actual_mappings.end();
+ ++expected_iter, ++actual_iter) {
+ EXPECT_EQ(expected_iter->email, actual_iter->email)
+ << "Verification Info: " << verification_info
+ << "; Account ID of expected: " << expected_iter->account_id;
+ EXPECT_EQ(expected_iter->account_id, actual_iter->account_id)
+ << "Verification Info: " << verification_info;
+ EXPECT_EQ(expected_iter->status, actual_iter->status)
+ << "Verification Info: " << verification_info
+ << "; Account ID of expected: " << expected_iter->account_id;
+ EXPECT_EQ(expected_iter->status_change_timestamp,
+ actual_iter->status_change_timestamp)
+ << "Verification Info: " << verification_info
+ << "; Account ID of expected: " << expected_iter->account_id;
+ }
+}
+
+class CustomFakeGCMDriver : public FakeGCMDriver {
+ public:
+ enum LastMessageAction {
+ NONE,
+ SEND_STARTED,
+ SEND_FINISHED,
+ SEND_ACKNOWLEDGED
+ };
+
+ CustomFakeGCMDriver();
+ ~CustomFakeGCMDriver() override;
+
+ // GCMDriver implementation:
+ void UpdateAccountMapping(const AccountMapping& account_mapping) override;
+ void RemoveAccountMapping(const CoreAccountId& account_id) override;
+ void RegisterImpl(const std::string& app_id,
+ const std::vector<std::string>& sender_ids) override;
+
+ void CompleteRegister(const std::string& registration_id,
+ GCMClient::Result result);
+ void CompleteSend(const std::string& message_id, GCMClient::Result result);
+ void AcknowledgeSend(const std::string& message_id);
+ void MessageSendError(const std::string& message_id);
+
+ void CompleteSendAllMessages();
+ void AcknowledgeSendAllMessages();
+ void SetLastMessageAction(const std::string& message_id,
+ LastMessageAction action);
+ void Clear();
+
+ const AccountMapping& last_account_mapping() const {
+ return account_mapping_;
+ }
+ const std::string& last_message_id() const { return last_message_id_; }
+ const CoreAccountId& last_removed_account_id() const {
+ return last_removed_account_id_;
+ }
+ LastMessageAction last_action() const { return last_action_; }
+ bool registration_id_requested() const { return registration_id_requested_; }
+
+ protected:
+ void SendImpl(const std::string& app_id,
+ const std::string& receiver_id,
+ const OutgoingMessage& message) override;
+
+ private:
+ AccountMapping account_mapping_;
+ std::string last_message_id_;
+ CoreAccountId last_removed_account_id_;
+ LastMessageAction last_action_;
+ std::map<std::string, LastMessageAction> all_messages_;
+ bool registration_id_requested_;
+};
+
+CustomFakeGCMDriver::CustomFakeGCMDriver()
+ : last_action_(NONE), registration_id_requested_(false) {
+}
+
+CustomFakeGCMDriver::~CustomFakeGCMDriver() {
+}
+
+void CustomFakeGCMDriver::UpdateAccountMapping(
+ const AccountMapping& account_mapping) {
+ account_mapping_.email = account_mapping.email;
+ account_mapping_.account_id = account_mapping.account_id;
+ account_mapping_.access_token = account_mapping.access_token;
+ account_mapping_.status = account_mapping.status;
+ account_mapping_.status_change_timestamp =
+ account_mapping.status_change_timestamp;
+ account_mapping_.last_message_id = account_mapping.last_message_id;
+}
+
+void CustomFakeGCMDriver::RemoveAccountMapping(
+ const CoreAccountId& account_id) {
+ last_removed_account_id_ = account_id;
+}
+
+void CustomFakeGCMDriver::RegisterImpl(
+ const std::string& app_id,
+ const std::vector<std::string>& sender_ids) {
+ DCHECK_EQ(kGCMAccountMapperAppId, app_id);
+ DCHECK_EQ(1u, sender_ids.size());
+ DCHECK_EQ(kGCMAccountMapperSenderId, sender_ids[0]);
+ registration_id_requested_ = true;
+}
+
+void CustomFakeGCMDriver::CompleteRegister(const std::string& registration_id,
+ GCMClient::Result result) {
+ RegisterFinished(kGCMAccountMapperAppId, registration_id, result);
+}
+
+void CustomFakeGCMDriver::CompleteSend(const std::string& message_id,
+ GCMClient::Result result) {
+ SendFinished(kGCMAccountMapperAppId, message_id, result);
+ SetLastMessageAction(message_id, SEND_FINISHED);
+}
+
+void CustomFakeGCMDriver::AcknowledgeSend(const std::string& message_id) {
+ GCMAppHandler* handler = GetAppHandler(kGCMAccountMapperAppId);
+ if (handler)
+ handler->OnSendAcknowledged(kGCMAccountMapperAppId, message_id);
+ SetLastMessageAction(message_id, SEND_ACKNOWLEDGED);
+}
+
+void CustomFakeGCMDriver::MessageSendError(const std::string& message_id) {
+ GCMAppHandler* handler = GetAppHandler(kGCMAccountMapperAppId);
+ if (!handler)
+ return;
+
+ GCMClient::SendErrorDetails send_error;
+ send_error.message_id = message_id;
+ send_error.result = GCMClient::TTL_EXCEEDED;
+
+ handler->OnSendError(kGCMAccountMapperAppId, send_error);
+}
+
+void CustomFakeGCMDriver::SendImpl(const std::string& app_id,
+ const std::string& receiver_id,
+ const OutgoingMessage& message) {
+ DCHECK_EQ(kGCMAccountMapperAppId, app_id);
+ DCHECK_EQ(kGCMAccountMapperSendTo, receiver_id);
+
+ SetLastMessageAction(message.id, SEND_STARTED);
+}
+
+void CustomFakeGCMDriver::CompleteSendAllMessages() {
+ for (std::map<std::string, LastMessageAction>::const_iterator iter =
+ all_messages_.begin();
+ iter != all_messages_.end();
+ ++iter) {
+ if (iter->second == SEND_STARTED)
+ CompleteSend(iter->first, GCMClient::SUCCESS);
+ }
+}
+
+void CustomFakeGCMDriver::AcknowledgeSendAllMessages() {
+ for (std::map<std::string, LastMessageAction>::const_iterator iter =
+ all_messages_.begin();
+ iter != all_messages_.end();
+ ++iter) {
+ if (iter->second == SEND_FINISHED)
+ AcknowledgeSend(iter->first);
+ }
+}
+
+void CustomFakeGCMDriver::Clear() {
+ account_mapping_ = AccountMapping();
+ last_message_id_.clear();
+ last_removed_account_id_ = CoreAccountId();
+ last_action_ = NONE;
+ registration_id_requested_ = false;
+}
+
+void CustomFakeGCMDriver::SetLastMessageAction(const std::string& message_id,
+ LastMessageAction action) {
+ last_action_ = action;
+ last_message_id_ = message_id;
+ all_messages_[message_id] = action;
+}
+
+} // namespace
+
+class GCMAccountMapperTest : public testing::Test {
+ public:
+ const CoreAccountId kAccountId;
+ const CoreAccountId kAccountId1;
+ const CoreAccountId kAccountId2;
+ const CoreAccountId kAccountId3;
+ const CoreAccountId kAccountId4;
+
+ GCMAccountMapperTest();
+ ~GCMAccountMapperTest() override;
+
+ void Restart();
+
+ void Initialize(const GCMAccountMapper::AccountMappings mappings);
+ const GCMAccountMapper::AccountMappings& GetAccounts() const {
+ return account_mapper_->accounts_;
+ }
+ void MessageReceived(const std::string& app_id,
+ const IncomingMessage& message);
+
+ GCMAccountMapper* mapper() { return account_mapper_.get(); }
+
+ CustomFakeGCMDriver& gcm_driver() { return gcm_driver_; }
+
+ base::SimpleTestClock* clock() { return &clock_; }
+ const std::string& last_received_app_id() const {
+ return last_received_app_id_;
+ }
+ const IncomingMessage& last_received_message() const {
+ return last_received_message_;
+ }
+
+ private:
+ CustomFakeGCMDriver gcm_driver_;
+ std::unique_ptr<GCMAccountMapper> account_mapper_;
+ base::SimpleTestClock clock_;
+ std::string last_received_app_id_;
+ IncomingMessage last_received_message_;
+};
+
+GCMAccountMapperTest::GCMAccountMapperTest()
+ : kAccountId("acc_id"),
+ kAccountId1("acc_id1"),
+ kAccountId2("acc_id2"),
+ kAccountId3("acc_id3"),
+ kAccountId4("acc_id4") {
+ Restart();
+}
+
+GCMAccountMapperTest::~GCMAccountMapperTest() {
+}
+
+void GCMAccountMapperTest::Restart() {
+ if (account_mapper_)
+ account_mapper_->ShutdownHandler();
+ gcm_driver_.RemoveAppHandler(kGCMAccountMapperAppId);
+ account_mapper_ = std::make_unique<GCMAccountMapper>(&gcm_driver_);
+ account_mapper_->SetClockForTesting(&clock_);
+}
+
+void GCMAccountMapperTest::Initialize(
+ const GCMAccountMapper::AccountMappings mappings) {
+ mapper()->Initialize(
+ mappings, base::BindRepeating(&GCMAccountMapperTest::MessageReceived,
+ base::Unretained(this)));
+}
+
+void GCMAccountMapperTest::MessageReceived(const std::string& app_id,
+ const IncomingMessage& message) {
+ last_received_app_id_ = app_id;
+ last_received_message_ = message;
+}
+
+// Tests the initialization of account mappings (from the store) when empty.
+// It also checks that initialization triggers registration ID request.
+TEST_F(GCMAccountMapperTest, InitializeAccountMappingsEmpty) {
+ EXPECT_FALSE(gcm_driver().registration_id_requested());
+ Initialize(GCMAccountMapper::AccountMappings());
+ EXPECT_TRUE(GetAccounts().empty());
+ EXPECT_TRUE(gcm_driver().registration_id_requested());
+}
+
+// Tests that registration is retried, when new tokens are delivered and in no
+// other circumstances.
+TEST_F(GCMAccountMapperTest, RegistrationRetryUponFailure) {
+ Initialize(GCMAccountMapper::AccountMappings());
+ EXPECT_TRUE(gcm_driver().registration_id_requested());
+ gcm_driver().Clear();
+
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::UNKNOWN_ERROR);
+ EXPECT_FALSE(gcm_driver().registration_id_requested());
+ gcm_driver().Clear();
+
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ account_tokens.push_back(MakeAccountTokenInfo(kAccountId2));
+ mapper()->SetAccountTokens(account_tokens);
+ EXPECT_TRUE(gcm_driver().registration_id_requested());
+ gcm_driver().Clear();
+
+ gcm_driver().CompleteRegister(kRegistrationId,
+ GCMClient::ASYNC_OPERATION_PENDING);
+ EXPECT_FALSE(gcm_driver().registration_id_requested());
+}
+
+// Tests the initialization of account mappings (from the store).
+TEST_F(GCMAccountMapperTest, InitializeAccountMappings) {
+ GCMAccountMapper::AccountMappings account_mappings;
+ AccountMapping account_mapping1 = MakeAccountMapping(
+ kAccountId1, AccountMapping::MAPPED, base::Time::Now(), std::string());
+ AccountMapping account_mapping2 = MakeAccountMapping(
+ kAccountId2, AccountMapping::ADDING, base::Time::Now(), "add_message_1");
+ account_mappings.push_back(account_mapping1);
+ account_mappings.push_back(account_mapping2);
+
+ Initialize(account_mappings);
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ EXPECT_EQ(2UL, mappings.size());
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+
+ EXPECT_EQ(account_mapping1.account_id, iter->account_id);
+ EXPECT_EQ(account_mapping1.email, iter->email);
+ EXPECT_TRUE(account_mapping1.access_token.empty());
+ EXPECT_EQ(account_mapping1.status, iter->status);
+ EXPECT_EQ(account_mapping1.status_change_timestamp,
+ iter->status_change_timestamp);
+ EXPECT_TRUE(account_mapping1.last_message_id.empty());
+
+ ++iter;
+ EXPECT_EQ(account_mapping2.account_id, iter->account_id);
+ EXPECT_EQ(account_mapping2.email, iter->email);
+ EXPECT_TRUE(account_mapping2.access_token.empty());
+ EXPECT_EQ(account_mapping2.status, iter->status);
+ EXPECT_EQ(account_mapping2.status_change_timestamp,
+ iter->status_change_timestamp);
+ EXPECT_EQ(account_mapping2.last_message_id, iter->last_message_id);
+}
+
+// Tests that account tokens are not processed until registration ID is
+// available.
+TEST_F(GCMAccountMapperTest, SetAccountTokensOnlyWorksWithRegisterationId) {
+ // Start with empty list.
+ Initialize(GCMAccountMapper::AccountMappings());
+
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ account_tokens.push_back(MakeAccountTokenInfo(kAccountId));
+ mapper()->SetAccountTokens(account_tokens);
+
+ EXPECT_TRUE(GetAccounts().empty());
+
+ account_tokens.clear();
+ account_tokens.push_back(MakeAccountTokenInfo(kAccountId1));
+ account_tokens.push_back(MakeAccountTokenInfo(kAccountId2));
+ mapper()->SetAccountTokens(account_tokens);
+
+ EXPECT_TRUE(GetAccounts().empty());
+
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ EXPECT_EQ(2UL, mappings.size());
+ EXPECT_EQ(kAccountId1, mappings[0].account_id);
+ EXPECT_EQ(kAccountId2, mappings[1].account_id);
+}
+
+// Tests the part where a new account is added with a token, to the point when
+// GCM message is sent.
+TEST_F(GCMAccountMapperTest, AddMappingToMessageSent) {
+ Initialize(GCMAccountMapper::AccountMappings());
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ GCMClient::AccountTokenInfo account_token = MakeAccountTokenInfo(kAccountId);
+ account_tokens.push_back(account_token);
+ mapper()->SetAccountTokens(account_tokens);
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ EXPECT_EQ(1UL, mappings.size());
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+ EXPECT_EQ(kAccountId, iter->account_id);
+ EXPECT_EQ("acc_id@gmail.com", iter->email);
+ EXPECT_EQ("acc_id_token", iter->access_token);
+ EXPECT_EQ(AccountMapping::NEW, iter->status);
+ EXPECT_EQ(base::Time(), iter->status_change_timestamp);
+
+ EXPECT_TRUE(!gcm_driver().last_message_id().empty());
+}
+
+// Tests the part where GCM message is successfully queued.
+TEST_F(GCMAccountMapperTest, AddMappingMessageQueued) {
+ Initialize(GCMAccountMapper::AccountMappings());
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ GCMClient::AccountTokenInfo account_token = MakeAccountTokenInfo(kAccountId);
+ account_tokens.push_back(account_token);
+ mapper()->SetAccountTokens(account_tokens);
+
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().CompleteSend(gcm_driver().last_message_id(), GCMClient::SUCCESS);
+
+ EXPECT_EQ(account_token.email, gcm_driver().last_account_mapping().email);
+ EXPECT_EQ(account_token.account_id,
+ gcm_driver().last_account_mapping().account_id);
+ EXPECT_EQ(account_token.access_token,
+ gcm_driver().last_account_mapping().access_token);
+ EXPECT_EQ(AccountMapping::ADDING, gcm_driver().last_account_mapping().status);
+ EXPECT_EQ(clock()->Now(),
+ gcm_driver().last_account_mapping().status_change_timestamp);
+ EXPECT_EQ(gcm_driver().last_message_id(),
+ gcm_driver().last_account_mapping().last_message_id);
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+ EXPECT_EQ(account_token.email, iter->email);
+ EXPECT_EQ(account_token.account_id, iter->account_id);
+ EXPECT_EQ(account_token.access_token, iter->access_token);
+ EXPECT_EQ(AccountMapping::ADDING, iter->status);
+ EXPECT_EQ(clock()->Now(), iter->status_change_timestamp);
+ EXPECT_EQ(gcm_driver().last_message_id(), iter->last_message_id);
+}
+
+// Tests status change from ADDING to MAPPED (Message is acknowledged).
+TEST_F(GCMAccountMapperTest, AddMappingMessageAcknowledged) {
+ Initialize(GCMAccountMapper::AccountMappings());
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ GCMClient::AccountTokenInfo account_token = MakeAccountTokenInfo(kAccountId);
+ account_tokens.push_back(account_token);
+ mapper()->SetAccountTokens(account_tokens);
+
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().CompleteSend(gcm_driver().last_message_id(), GCMClient::SUCCESS);
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().AcknowledgeSend(gcm_driver().last_message_id());
+
+ EXPECT_EQ(account_token.email, gcm_driver().last_account_mapping().email);
+ EXPECT_EQ(account_token.account_id,
+ gcm_driver().last_account_mapping().account_id);
+ EXPECT_EQ(account_token.access_token,
+ gcm_driver().last_account_mapping().access_token);
+ EXPECT_EQ(AccountMapping::MAPPED, gcm_driver().last_account_mapping().status);
+ EXPECT_EQ(clock()->Now(),
+ gcm_driver().last_account_mapping().status_change_timestamp);
+ EXPECT_TRUE(gcm_driver().last_account_mapping().last_message_id.empty());
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+ EXPECT_EQ(account_token.email, iter->email);
+ EXPECT_EQ(account_token.account_id, iter->account_id);
+ EXPECT_EQ(account_token.access_token, iter->access_token);
+ EXPECT_EQ(AccountMapping::MAPPED, iter->status);
+ EXPECT_EQ(clock()->Now(), iter->status_change_timestamp);
+ EXPECT_TRUE(iter->last_message_id.empty());
+}
+
+// Tests status change form ADDING to MAPPED (When message was acknowledged,
+// after Chrome was restarted).
+TEST_F(GCMAccountMapperTest, AddMappingMessageAckedAfterRestart) {
+ Initialize(GCMAccountMapper::AccountMappings());
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ GCMClient::AccountTokenInfo account_token = MakeAccountTokenInfo(kAccountId);
+ account_tokens.push_back(account_token);
+ mapper()->SetAccountTokens(account_tokens);
+
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().CompleteSend(gcm_driver().last_message_id(), GCMClient::SUCCESS);
+
+ Restart();
+ GCMAccountMapper::AccountMappings stored_mappings;
+ stored_mappings.push_back(gcm_driver().last_account_mapping());
+ Initialize(stored_mappings);
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().AcknowledgeSend(gcm_driver().last_message_id());
+
+ EXPECT_EQ(account_token.email, gcm_driver().last_account_mapping().email);
+ EXPECT_EQ(account_token.account_id,
+ gcm_driver().last_account_mapping().account_id);
+ EXPECT_EQ(account_token.access_token,
+ gcm_driver().last_account_mapping().access_token);
+ EXPECT_EQ(AccountMapping::MAPPED, gcm_driver().last_account_mapping().status);
+ EXPECT_EQ(clock()->Now(),
+ gcm_driver().last_account_mapping().status_change_timestamp);
+ EXPECT_TRUE(gcm_driver().last_account_mapping().last_message_id.empty());
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+ EXPECT_EQ(account_token.email, iter->email);
+ EXPECT_EQ(account_token.account_id, iter->account_id);
+ EXPECT_EQ(account_token.access_token, iter->access_token);
+ EXPECT_EQ(AccountMapping::MAPPED, iter->status);
+ EXPECT_EQ(clock()->Now(), iter->status_change_timestamp);
+ EXPECT_TRUE(iter->last_message_id.empty());
+}
+
+// Tests a case when ADD message times out for a new account.
+TEST_F(GCMAccountMapperTest, AddMappingMessageSendErrorForNewAccount) {
+ Initialize(GCMAccountMapper::AccountMappings());
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ GCMClient::AccountTokenInfo account_token = MakeAccountTokenInfo(kAccountId);
+ account_tokens.push_back(account_token);
+ mapper()->SetAccountTokens(account_tokens);
+
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().CompleteSend(gcm_driver().last_message_id(), GCMClient::SUCCESS);
+
+ clock()->SetNow(base::Time::Now());
+ std::string old_message_id = gcm_driver().last_message_id();
+ gcm_driver().MessageSendError(old_message_id);
+
+ // No new message is sent because of the send error, as the token is stale.
+ // Because the account was new, the entry should be deleted.
+ EXPECT_EQ(old_message_id, gcm_driver().last_message_id());
+ EXPECT_EQ(account_token.account_id, gcm_driver().last_removed_account_id());
+ EXPECT_TRUE(GetAccounts().empty());
+}
+
+/// Tests a case when ADD message times out for a MAPPED account.
+TEST_F(GCMAccountMapperTest, AddMappingMessageSendErrorForMappedAccount) {
+ // Start with one account that is mapped.
+ base::Time status_change_timestamp = base::Time::Now();
+ AccountMapping mapping =
+ MakeAccountMapping(kAccountId, AccountMapping::MAPPED,
+ status_change_timestamp, "add_message_id");
+
+ GCMAccountMapper::AccountMappings stored_mappings;
+ stored_mappings.push_back(mapping);
+ Initialize(stored_mappings);
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().MessageSendError("add_message_id");
+
+ // No new message is sent because of the send error, as the token is stale.
+ // Because the account was new, the entry should be deleted.
+ EXPECT_TRUE(gcm_driver().last_message_id().empty());
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+ EXPECT_EQ(mapping.email, iter->email);
+ EXPECT_EQ(mapping.account_id, iter->account_id);
+ EXPECT_EQ(mapping.access_token, iter->access_token);
+ EXPECT_EQ(AccountMapping::MAPPED, iter->status);
+ EXPECT_EQ(status_change_timestamp, iter->status_change_timestamp);
+ EXPECT_TRUE(iter->last_message_id.empty());
+}
+
+// Tests that a missing token for an account will trigger removing of that
+// account. This test goes only until the message is passed to GCM.
+TEST_F(GCMAccountMapperTest, RemoveMappingToMessageSent) {
+ // Start with one account that is mapped.
+ AccountMapping mapping = MakeAccountMapping(
+ kAccountId, AccountMapping::MAPPED, base::Time::Now(), std::string());
+
+ GCMAccountMapper::AccountMappings stored_mappings;
+ stored_mappings.push_back(mapping);
+ Initialize(stored_mappings);
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+ clock()->SetNow(base::Time::Now());
+
+ mapper()->SetAccountTokens(std::vector<GCMClient::AccountTokenInfo>());
+
+ EXPECT_EQ(mapping.account_id, gcm_driver().last_account_mapping().account_id);
+ EXPECT_EQ(mapping.email, gcm_driver().last_account_mapping().email);
+ EXPECT_EQ(AccountMapping::REMOVING,
+ gcm_driver().last_account_mapping().status);
+ EXPECT_EQ(clock()->Now(),
+ gcm_driver().last_account_mapping().status_change_timestamp);
+ EXPECT_TRUE(gcm_driver().last_account_mapping().last_message_id.empty());
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+ EXPECT_EQ(mapping.email, iter->email);
+ EXPECT_EQ(mapping.account_id, iter->account_id);
+ EXPECT_EQ(mapping.access_token, iter->access_token);
+ EXPECT_EQ(AccountMapping::REMOVING, iter->status);
+ EXPECT_EQ(clock()->Now(), iter->status_change_timestamp);
+ EXPECT_TRUE(iter->last_message_id.empty());
+}
+
+// Tests that a missing token for an account will trigger removing of that
+// account. This test goes until the message is queued by GCM.
+TEST_F(GCMAccountMapperTest, RemoveMappingMessageQueued) {
+ // Start with one account that is mapped.
+ AccountMapping mapping = MakeAccountMapping(
+ kAccountId, AccountMapping::MAPPED, base::Time::Now(), std::string());
+
+ GCMAccountMapper::AccountMappings stored_mappings;
+ stored_mappings.push_back(mapping);
+ Initialize(stored_mappings);
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+ clock()->SetNow(base::Time::Now());
+ base::Time status_change_timestamp = clock()->Now();
+
+ mapper()->SetAccountTokens(std::vector<GCMClient::AccountTokenInfo>());
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().CompleteSend(gcm_driver().last_message_id(), GCMClient::SUCCESS);
+
+ EXPECT_EQ(mapping.account_id, gcm_driver().last_account_mapping().account_id);
+ EXPECT_EQ(mapping.email, gcm_driver().last_account_mapping().email);
+ EXPECT_EQ(AccountMapping::REMOVING,
+ gcm_driver().last_account_mapping().status);
+ EXPECT_EQ(status_change_timestamp,
+ gcm_driver().last_account_mapping().status_change_timestamp);
+ EXPECT_TRUE(!gcm_driver().last_account_mapping().last_message_id.empty());
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+ EXPECT_EQ(mapping.email, iter->email);
+ EXPECT_EQ(mapping.account_id, iter->account_id);
+ EXPECT_EQ(mapping.access_token, iter->access_token);
+ EXPECT_EQ(AccountMapping::REMOVING, iter->status);
+ EXPECT_EQ(status_change_timestamp, iter->status_change_timestamp);
+ EXPECT_EQ(gcm_driver().last_account_mapping().last_message_id,
+ iter->last_message_id);
+}
+
+// Tests that a missing token for an account will trigger removing of that
+// account. This test goes until the message is acknowledged by GCM.
+// This is a complete success scenario for account removal, and it end with
+// account mapping being completely gone.
+TEST_F(GCMAccountMapperTest, RemoveMappingMessageAcknowledged) {
+ // Start with one account that is mapped.
+ AccountMapping mapping = MakeAccountMapping(
+ kAccountId, AccountMapping::MAPPED, base::Time::Now(), std::string());
+
+ GCMAccountMapper::AccountMappings stored_mappings;
+ stored_mappings.push_back(mapping);
+ Initialize(stored_mappings);
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+ clock()->SetNow(base::Time::Now());
+
+ mapper()->SetAccountTokens(std::vector<GCMClient::AccountTokenInfo>());
+ gcm_driver().CompleteSend(gcm_driver().last_message_id(), GCMClient::SUCCESS);
+ gcm_driver().AcknowledgeSend(gcm_driver().last_message_id());
+
+ EXPECT_EQ(mapping.account_id, gcm_driver().last_removed_account_id());
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ EXPECT_TRUE(mappings.empty());
+}
+
+// Tests that account removing proceeds, when a removing message is acked after
+// Chrome was restarted.
+TEST_F(GCMAccountMapperTest, RemoveMappingMessageAckedAfterRestart) {
+ // Start with one account that is mapped.
+ AccountMapping mapping =
+ MakeAccountMapping(kAccountId, AccountMapping::REMOVING,
+ base::Time::Now(), "remove_message_id");
+
+ GCMAccountMapper::AccountMappings stored_mappings;
+ stored_mappings.push_back(mapping);
+ Initialize(stored_mappings);
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+
+ gcm_driver().AcknowledgeSend("remove_message_id");
+
+ EXPECT_EQ(mapping.account_id, gcm_driver().last_removed_account_id());
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ EXPECT_TRUE(mappings.empty());
+}
+
+// Tests that account removing proceeds, when a removing message is acked after
+// Chrome was restarted.
+TEST_F(GCMAccountMapperTest, RemoveMappingMessageSendError) {
+ // Start with one account that is mapped.
+ base::Time status_change_timestamp = base::Time::Now();
+ AccountMapping mapping =
+ MakeAccountMapping(kAccountId, AccountMapping::REMOVING,
+ status_change_timestamp, "remove_message_id");
+
+ GCMAccountMapper::AccountMappings stored_mappings;
+ stored_mappings.push_back(mapping);
+ Initialize(stored_mappings);
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().MessageSendError("remove_message_id");
+
+ EXPECT_TRUE(gcm_driver().last_removed_account_id().empty());
+
+ EXPECT_EQ(mapping.account_id, gcm_driver().last_account_mapping().account_id);
+ EXPECT_EQ(mapping.email, gcm_driver().last_account_mapping().email);
+ EXPECT_EQ(AccountMapping::REMOVING,
+ gcm_driver().last_account_mapping().status);
+ EXPECT_EQ(status_change_timestamp,
+ gcm_driver().last_account_mapping().status_change_timestamp);
+ // Message is not persisted, until send is completed.
+ EXPECT_TRUE(gcm_driver().last_account_mapping().last_message_id.empty());
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+ EXPECT_EQ(mapping.email, iter->email);
+ EXPECT_EQ(mapping.account_id, iter->account_id);
+ EXPECT_TRUE(iter->access_token.empty());
+ EXPECT_EQ(AccountMapping::REMOVING, iter->status);
+ EXPECT_EQ(status_change_timestamp, iter->status_change_timestamp);
+ EXPECT_TRUE(iter->last_message_id.empty());
+}
+
+// Tests that, if a new token arrives when the adding message is in progress
+// no new message is sent and account mapper still waits for the first one to
+// complete.
+TEST_F(GCMAccountMapperTest, TokenIsRefreshedWhenAdding) {
+ Initialize(GCMAccountMapper::AccountMappings());
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+
+ clock()->SetNow(base::Time::Now());
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ GCMClient::AccountTokenInfo account_token = MakeAccountTokenInfo(kAccountId);
+ account_tokens.push_back(account_token);
+ mapper()->SetAccountTokens(account_tokens);
+ DCHECK_EQ(CustomFakeGCMDriver::SEND_STARTED, gcm_driver().last_action());
+
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().CompleteSend(gcm_driver().last_message_id(), GCMClient::SUCCESS);
+ DCHECK_EQ(CustomFakeGCMDriver::SEND_FINISHED, gcm_driver().last_action());
+
+ // Providing another token and clearing status.
+ gcm_driver().Clear();
+ mapper()->SetAccountTokens(account_tokens);
+ DCHECK_EQ(CustomFakeGCMDriver::NONE, gcm_driver().last_action());
+}
+
+// Tests that, if a new token arrives when a removing message is in progress
+// a new adding message is sent and while account mapping status is changed to
+// mapped. If the original Removing message arrives it is discarded.
+TEST_F(GCMAccountMapperTest, TokenIsRefreshedWhenRemoving) {
+ // Start with one account that is mapped.
+ AccountMapping mapping = MakeAccountMapping(
+ kAccountId, AccountMapping::MAPPED, base::Time::Now(), std::string());
+
+ GCMAccountMapper::AccountMappings stored_mappings;
+ stored_mappings.push_back(mapping);
+ Initialize(stored_mappings);
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+ clock()->SetNow(base::Time::Now());
+
+ // Remove the token to trigger a remove message to be sent
+ mapper()->SetAccountTokens(std::vector<GCMClient::AccountTokenInfo>());
+ EXPECT_EQ(CustomFakeGCMDriver::SEND_STARTED, gcm_driver().last_action());
+ gcm_driver().CompleteSend(gcm_driver().last_message_id(), GCMClient::SUCCESS);
+ EXPECT_EQ(CustomFakeGCMDriver::SEND_FINISHED, gcm_driver().last_action());
+
+ std::string remove_message_id = gcm_driver().last_message_id();
+ gcm_driver().Clear();
+
+ // The account mapping for acc_id is now in status REMOVING.
+ // Adding the token for that account.
+ clock()->SetNow(base::Time::Now());
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ GCMClient::AccountTokenInfo account_token = MakeAccountTokenInfo(kAccountId);
+ account_tokens.push_back(account_token);
+ mapper()->SetAccountTokens(account_tokens);
+ DCHECK_EQ(CustomFakeGCMDriver::SEND_STARTED, gcm_driver().last_action());
+ gcm_driver().CompleteSend(gcm_driver().last_message_id(), GCMClient::SUCCESS);
+ EXPECT_EQ(CustomFakeGCMDriver::SEND_FINISHED, gcm_driver().last_action());
+
+ std::string add_message_id = gcm_driver().last_message_id();
+
+ // A remove message confirmation arrives now, but should be ignored.
+ gcm_driver().AcknowledgeSend(remove_message_id);
+
+ GCMAccountMapper::AccountMappings mappings = GetAccounts();
+ GCMAccountMapper::AccountMappings::const_iterator iter = mappings.begin();
+ EXPECT_EQ(mapping.email, iter->email);
+ EXPECT_EQ(mapping.account_id, iter->account_id);
+ EXPECT_FALSE(iter->access_token.empty());
+ EXPECT_EQ(AccountMapping::MAPPED, iter->status);
+ // Status change timestamp is set to very long time ago, to make sure the next
+ // round of mapping picks it up.
+ EXPECT_EQ(base::Time(), iter->status_change_timestamp);
+ EXPECT_EQ(add_message_id, iter->last_message_id);
+}
+
+// Tests adding/removing works for multiple accounts, after a restart and when
+// tokens are periodically delierverd.
+TEST_F(GCMAccountMapperTest, MultipleAccountMappings) {
+ clock()->SetNow(base::Time::Now());
+ base::Time half_hour_ago = clock()->Now() - base::Minutes(30);
+ GCMAccountMapper::AccountMappings stored_mappings;
+ stored_mappings.push_back(MakeAccountMapping(
+ kAccountId, AccountMapping::ADDING, half_hour_ago, "acc_id_msg"));
+ stored_mappings.push_back(MakeAccountMapping(
+ kAccountId1, AccountMapping::MAPPED, half_hour_ago, "acc_id_1_msg"));
+ stored_mappings.push_back(MakeAccountMapping(
+ kAccountId2, AccountMapping::REMOVING, half_hour_ago, "acc_id_2_msg"));
+
+ Initialize(stored_mappings);
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+ gcm_driver().CompleteRegister(kRegistrationId, GCMClient::SUCCESS);
+
+ GCMAccountMapper::AccountMappings expected_mappings(stored_mappings);
+
+ // Finish messages after a restart.
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().AcknowledgeSend(expected_mappings[0].last_message_id);
+ expected_mappings[0].status_change_timestamp = clock()->Now();
+ expected_mappings[0].status = AccountMapping::MAPPED;
+ expected_mappings[0].last_message_id.clear();
+
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().AcknowledgeSend(expected_mappings[1].last_message_id);
+ expected_mappings[1].status_change_timestamp = clock()->Now();
+ expected_mappings[1].status = AccountMapping::MAPPED;
+ expected_mappings[1].last_message_id.clear();
+
+ // Upon success last element is removed.
+ clock()->SetNow(base::Time::Now());
+ gcm_driver().AcknowledgeSend(expected_mappings[2].last_message_id);
+ expected_mappings.pop_back();
+
+ VerifyMappings(expected_mappings, GetAccounts(), "Step 1, After restart");
+
+ // One of accounts gets removed.
+ std::vector<GCMClient::AccountTokenInfo> account_tokens;
+ account_tokens.push_back(MakeAccountTokenInfo(kAccountId));
+
+ // Advance a day to make sure existing mappings will be reported.
+ clock()->SetNow(clock()->Now() + base::Days(1));
+ mapper()->SetAccountTokens(account_tokens);
+
+ expected_mappings[0].status = AccountMapping::MAPPED;
+ expected_mappings[1].status = AccountMapping::REMOVING;
+ expected_mappings[1].status_change_timestamp = clock()->Now();
+
+ gcm_driver().CompleteSendAllMessages();
+
+ VerifyMappings(
+ expected_mappings, GetAccounts(), "Step 2, One account is being removed");
+
+ clock()->SetNow(clock()->Now() + base::Seconds(5));
+ gcm_driver().AcknowledgeSendAllMessages();
+
+ expected_mappings[0].status_change_timestamp = clock()->Now();
+ expected_mappings.pop_back();
+
+ VerifyMappings(
+ expected_mappings, GetAccounts(), "Step 3, Removing completed");
+
+ account_tokens.clear();
+ account_tokens.push_back(MakeAccountTokenInfo(kAccountId));
+ account_tokens.push_back(MakeAccountTokenInfo(kAccountId3));
+ account_tokens.push_back(MakeAccountTokenInfo(kAccountId4));
+
+ // Advance a day to make sure existing mappings will be reported.
+ clock()->SetNow(clock()->Now() + base::Days(1));
+ mapper()->SetAccountTokens(account_tokens);
+
+ // Mapping from acc_id_0 still in position 0
+ expected_mappings.push_back(MakeAccountMapping(
+ kAccountId3, AccountMapping::NEW, base::Time(), std::string()));
+ expected_mappings.push_back(MakeAccountMapping(
+ kAccountId4, AccountMapping::NEW, base::Time(), std::string()));
+
+ VerifyMappings(expected_mappings, GetAccounts(), "Step 4, Two new accounts");
+
+ clock()->SetNow(clock()->Now() + base::Seconds(1));
+ gcm_driver().CompleteSendAllMessages();
+
+ expected_mappings[1].status = AccountMapping::ADDING;
+ expected_mappings[1].status_change_timestamp = clock()->Now();
+ expected_mappings[2].status = AccountMapping::ADDING;
+ expected_mappings[2].status_change_timestamp = clock()->Now();
+
+ VerifyMappings(
+ expected_mappings, GetAccounts(), "Step 5, Two accounts being added");
+
+ clock()->SetNow(clock()->Now() + base::Seconds(5));
+ gcm_driver().AcknowledgeSendAllMessages();
+
+ expected_mappings[0].status_change_timestamp = clock()->Now();
+ expected_mappings[1].status_change_timestamp = clock()->Now();
+ expected_mappings[1].status = AccountMapping::MAPPED;
+ expected_mappings[2].status_change_timestamp = clock()->Now();
+ expected_mappings[2].status = AccountMapping::MAPPED;
+
+ VerifyMappings(
+ expected_mappings, GetAccounts(), "Step 6, Three mapped accounts");
+}
+
+TEST_F(GCMAccountMapperTest, DispatchMessageSentToGaiaID) {
+ Initialize(GCMAccountMapper::AccountMappings());
+ gcm_driver().AddAppHandler(kGCMAccountMapperAppId, mapper());
+ IncomingMessage message;
+ message.data[kEmbeddedAppIdKey] = kTestAppId;
+ message.data[kTestDataKey] = kTestDataValue;
+ message.collapse_key = kTestCollapseKey;
+ message.sender_id = kTestSenderId;
+ mapper()->OnMessage(kGCMAccountMapperAppId, message);
+
+ EXPECT_EQ(kTestAppId, last_received_app_id());
+ EXPECT_EQ(1UL, last_received_message().data.size());
+ auto it = last_received_message().data.find(kTestDataKey);
+ EXPECT_TRUE(it != last_received_message().data.end());
+ EXPECT_EQ(kTestDataValue, it->second);
+ EXPECT_EQ(kTestCollapseKey, last_received_message().collapse_key);
+ EXPECT_EQ(kTestSenderId, last_received_message().sender_id);
+}
+
+} // namespace gcm