summaryrefslogtreecommitdiffstats
path: root/google_apis/gcm
diff options
context:
space:
mode:
authorfgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-16 16:05:36 +0000
committerfgorski@chromium.org <fgorski@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-16 16:05:36 +0000
commit79994f49494568039b0c1e310bb6ffd165d62c01 (patch)
tree27c560961dd22b9747952c4e0cb2a018b52dc685 /google_apis/gcm
parent348a2a79dd91a7f2f4007137bae4a48d57debd27 (diff)
downloadchromium_src-79994f49494568039b0c1e310bb6ffd165d62c01.zip
chromium_src-79994f49494568039b0c1e310bb6ffd165d62c01.tar.gz
chromium_src-79994f49494568039b0c1e310bb6ffd165d62c01.tar.bz2
Adding a user list (to be consumed by GCM Client Implementation)
The goal of the list is to store mappings between usernames, serial numbers and delegates. Patch includes: * implementation of the user list * tests for the user list BUG=284553 Review URL: https://codereview.chromium.org/135303002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245207 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'google_apis/gcm')
-rw-r--r--google_apis/gcm/engine/gcm_store.cc11
-rw-r--r--google_apis/gcm/engine/gcm_store.h12
-rw-r--r--google_apis/gcm/engine/gcm_store_impl.cc11
-rw-r--r--google_apis/gcm/engine/gcm_store_impl_unittest.cc25
-rw-r--r--google_apis/gcm/engine/user_list.cc197
-rw-r--r--google_apis/gcm/engine/user_list.h102
-rw-r--r--google_apis/gcm/engine/user_list_unittest.cc370
-rw-r--r--google_apis/gcm/gcm.gyp3
-rw-r--r--google_apis/gcm/gcm_client_impl.cc13
-rw-r--r--google_apis/gcm/gcm_client_impl.h17
10 files changed, 743 insertions, 18 deletions
diff --git a/google_apis/gcm/engine/gcm_store.cc b/google_apis/gcm/engine/gcm_store.cc
index b8559b0..f6c81dd 100644
--- a/google_apis/gcm/engine/gcm_store.cc
+++ b/google_apis/gcm/engine/gcm_store.cc
@@ -6,11 +6,18 @@
namespace gcm {
+GCMStore::SerialNumberMappings::SerialNumberMappings()
+ : next_serial_number(1LL) {
+}
+
+GCMStore::SerialNumberMappings::~SerialNumberMappings() {
+}
+
GCMStore::LoadResult::LoadResult()
: success(false),
device_android_id(0),
- device_security_token(0),
- next_serial_number(1LL) {}
+ device_security_token(0) {
+}
GCMStore::LoadResult::~LoadResult() {}
diff --git a/google_apis/gcm/engine/gcm_store.h b/google_apis/gcm/engine/gcm_store.h
index 06189a9..d26ca18 100644
--- a/google_apis/gcm/engine/gcm_store.h
+++ b/google_apis/gcm/engine/gcm_store.h
@@ -28,6 +28,15 @@ class MCSMessage;
// as well as store device and user checkin information.
class GCM_EXPORT GCMStore {
public:
+ // Part of load results storing user serial number mapping related values.
+ struct GCM_EXPORT SerialNumberMappings {
+ SerialNumberMappings();
+ ~SerialNumberMappings();
+
+ int64 next_serial_number;
+ std::map<std::string, int64> user_serial_numbers;
+ };
+
// Container for Load(..) results.
struct GCM_EXPORT LoadResult {
LoadResult();
@@ -38,8 +47,7 @@ class GCM_EXPORT GCMStore {
uint64 device_security_token;
std::vector<std::string> incoming_messages;
std::map<std::string, google::protobuf::MessageLite*> outgoing_messages;
- int64 next_serial_number;
- std::map<std::string, int64> user_serial_numbers;
+ SerialNumberMappings serial_number_mappings;
};
typedef std::vector<std::string> PersistentIdList;
diff --git a/google_apis/gcm/engine/gcm_store_impl.cc b/google_apis/gcm/engine/gcm_store_impl.cc
index 9edb219..90019b9 100644
--- a/google_apis/gcm/engine/gcm_store_impl.cc
+++ b/google_apis/gcm/engine/gcm_store_impl.cc
@@ -169,8 +169,10 @@ void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
&result.device_security_token) ||
!LoadIncomingMessages(&result.incoming_messages) ||
!LoadOutgoingMessages(&result.outgoing_messages) ||
- !LoadNextSerialNumber(&result.next_serial_number) ||
- !LoadUserSerialNumberMap(&result.user_serial_numbers)) {
+ !LoadNextSerialNumber(
+ &result.serial_number_mappings.next_serial_number) ||
+ !LoadUserSerialNumberMap(
+ &result.serial_number_mappings.user_serial_numbers)) {
result.device_android_id = 0;
result.device_security_token = 0;
result.incoming_messages.clear();
@@ -192,10 +194,11 @@ void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
result.outgoing_messages.size());
UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
result.incoming_messages.size());
- UMA_HISTOGRAM_COUNTS("GCM.NumUsers", result.user_serial_numbers.size());
+ UMA_HISTOGRAM_COUNTS(
+ "GCM.NumUsers",
+ result.serial_number_mappings.user_serial_numbers.size());
}
-
DVLOG(1) << "Succeeded in loading " << result.incoming_messages.size()
<< " unacknowledged incoming messages and "
<< result.outgoing_messages.size()
diff --git a/google_apis/gcm/engine/gcm_store_impl_unittest.cc b/google_apis/gcm/engine/gcm_store_impl_unittest.cc
index 5b28ef7..c3dbcb3 100644
--- a/google_apis/gcm/engine/gcm_store_impl_unittest.cc
+++ b/google_apis/gcm/engine/gcm_store_impl_unittest.cc
@@ -109,8 +109,8 @@ TEST_F(GCMStoreImplTest, LoadNew) {
EXPECT_EQ(0U, load_result.device_security_token);
EXPECT_TRUE(load_result.incoming_messages.empty());
EXPECT_TRUE(load_result.outgoing_messages.empty());
- EXPECT_EQ(1LL, load_result.next_serial_number);
- EXPECT_TRUE(load_result.user_serial_numbers.empty());
+ EXPECT_EQ(1LL, load_result.serial_number_mappings.next_serial_number);
+ EXPECT_TRUE(load_result.serial_number_mappings.user_serial_numbers.empty());
}
TEST_F(GCMStoreImplTest, DeviceCredentials) {
@@ -314,7 +314,8 @@ TEST_F(GCMStoreImplTest, NextSerialNumber) {
&GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
PumpLoop();
- EXPECT_EQ(kNextSerialNumber, load_result.next_serial_number);
+ EXPECT_EQ(kNextSerialNumber,
+ load_result.serial_number_mappings.next_serial_number);
}
// Verify that user serial number mappings are persisted properly.
@@ -345,13 +346,17 @@ TEST_F(GCMStoreImplTest, UserSerialNumberMappings) {
&GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
PumpLoop();
- ASSERT_EQ(2u, load_result.user_serial_numbers.size());
- ASSERT_NE(load_result.user_serial_numbers.end(),
- load_result.user_serial_numbers.find(username1));
- EXPECT_EQ(serial_number1, load_result.user_serial_numbers[username1]);
- ASSERT_NE(load_result.user_serial_numbers.end(),
- load_result.user_serial_numbers.find(username2));
- EXPECT_EQ(serial_number2, load_result.user_serial_numbers[username2]);
+ ASSERT_EQ(2u, load_result.serial_number_mappings.user_serial_numbers.size());
+ ASSERT_NE(
+ load_result.serial_number_mappings.user_serial_numbers.end(),
+ load_result.serial_number_mappings.user_serial_numbers.find(username1));
+ EXPECT_EQ(serial_number1,
+ load_result.serial_number_mappings.user_serial_numbers[username1]);
+ ASSERT_NE(
+ load_result.serial_number_mappings.user_serial_numbers.end(),
+ load_result.serial_number_mappings.user_serial_numbers.find(username2));
+ EXPECT_EQ(serial_number2,
+ load_result.serial_number_mappings.user_serial_numbers[username2]);
}
// Test that per-app message limits are enforced, persisted across restarts,
diff --git a/google_apis/gcm/engine/user_list.cc b/google_apis/gcm/engine/user_list.cc
new file mode 100644
index 0000000..829c66f
--- /dev/null
+++ b/google_apis/gcm/engine/user_list.cc
@@ -0,0 +1,197 @@
+// 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 "google_apis/gcm/engine/user_list.h"
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "third_party/leveldatabase/src/include/leveldb/db.h"
+
+namespace gcm {
+const int64 kInvalidSerialNumber = -1;
+
+UserList::UserInfo::UserInfo()
+ : serial_number(kInvalidSerialNumber),
+ delegate(NULL) {
+}
+
+UserList::UserInfo::UserInfo(int64 serial_number)
+ : serial_number(serial_number),
+ delegate(NULL) {
+}
+
+UserList::UserInfo::UserInfo(GCMClient::Delegate* delegate,
+ const SetDelegateCallback& callback)
+ : serial_number(kInvalidSerialNumber),
+ delegate(delegate),
+ callback(callback) {
+}
+
+UserList::UserInfo::~UserInfo() {
+}
+
+UserList::UserList(GCMStore* gcm_store)
+ : initialized_(false),
+ next_serial_number_(kInvalidSerialNumber),
+ gcm_store_(gcm_store) {
+}
+
+UserList::~UserList() {
+}
+
+void UserList::Initialize(const GCMStore::SerialNumberMappings& result) {
+ DCHECK(!initialized_);
+ DCHECK_GT(result.next_serial_number, 0);
+
+ initialized_ = true;
+ next_serial_number_ = result.next_serial_number;
+ for (std::map<std::string, int64>::const_iterator iter =
+ result.user_serial_numbers.begin();
+ iter != result.user_serial_numbers.end();
+ ++iter) {
+ SetSerialNumber(iter->first, iter->second);
+ }
+
+ for (UserInfoMap::iterator iter = delegates_.begin();
+ iter != delegates_.end();
+ ++iter) {
+ // Process only users with delegate and callback present.
+ if (iter->second.delegate && !iter->second.callback.is_null()) {
+ if (iter->second.serial_number == kInvalidSerialNumber)
+ AssignSerialNumber(iter->first);
+ else
+ OnSerialNumberReady(iter);
+ }
+ }
+}
+
+void UserList::SetDelegate(const std::string& username,
+ GCMClient::Delegate* delegate,
+ const SetDelegateCallback& callback) {
+ DCHECK(!username.empty());
+ DCHECK(delegate);
+
+ UserInfoMap::iterator iter = delegates_.find(username);
+ if (iter != delegates_.end()) {
+ DCHECK(initialized_);
+ DCHECK(!iter->second.delegate);
+ iter->second.delegate = delegate;
+ iter->second.callback = callback;
+ } else {
+ delegates_.insert(
+ iter,
+ UserInfoMap::value_type(username, UserInfo(delegate, callback)));
+ }
+
+ if (initialized_) {
+ UserInfoMap::iterator iter = delegates_.find(username);
+ if (iter->second.serial_number == kInvalidSerialNumber)
+ AssignSerialNumber(iter->first);
+ else
+ OnSerialNumberReady(iter);
+ }
+}
+
+void UserList::AssignSerialNumber(const std::string& username) {
+ DCHECK(initialized_);
+ DCHECK_EQ(delegates_.count(username), 1UL);
+ DCHECK_EQ(delegates_[username].serial_number, kInvalidSerialNumber);
+
+ // Start by incrementing the |next_serial_number_| and persist it in the GCM
+ // store.
+ int64 serial_number = next_serial_number_++;
+ gcm_store_->SetNextSerialNumber(
+ serial_number,
+ base::Bind(&UserList::IncrementSerialNumberCompleted,
+ base::Unretained(this),
+ username,
+ serial_number));
+}
+
+void UserList::IncrementSerialNumberCompleted(const std::string& username,
+ int64 user_serial_number,
+ bool success) {
+ // TODO(fgorski): Introduce retry logic.
+ if (!success)
+ DCHECK(success) << "Updating the next serial number failed.";
+
+ SetSerialNumber(username, user_serial_number);
+ gcm_store_->AddUserSerialNumber(
+ username,
+ user_serial_number,
+ base::Bind(&UserList::AssignSerialNumberCompleted,
+ base::Unretained(this),
+ username));
+}
+
+void UserList::AssignSerialNumberCompleted(const std::string& username,
+ bool success) {
+ // TODO(fgorski): Introduce retry logic.
+ if (!success) {
+ DVLOG(1) << "It was not possible to persist username to serial number"
+ << " mapping for username: " << username;
+ return;
+ }
+
+ UserInfoMap::iterator iter = delegates_.find(username);
+ DCHECK(iter != delegates_.end());
+ OnSerialNumberReady(iter);
+}
+
+void UserList::OnSerialNumberReady(const UserInfoMap::iterator& iter) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(iter->second.callback,
+ iter->first,
+ iter->second.serial_number));
+ iter->second.callback.Reset();
+}
+
+void UserList::SetSerialNumber(const std::string& username,
+ int64 serial_number) {
+ DCHECK(!username.empty());
+ DCHECK_GT(serial_number, 0L);
+
+ UserInfoMap::iterator iter = delegates_.find(username);
+ if (iter != delegates_.end()) {
+ DCHECK_EQ(iter->second.serial_number, kInvalidSerialNumber);
+ iter->second.serial_number = serial_number;
+ } else {
+ delegates_.insert(
+ iter, UserInfoMap::value_type(username, UserInfo(serial_number)));
+ }
+}
+
+GCMClient::Delegate* UserList::GetDelegateBySerialNumber(int64 serial_number)
+ const {
+ for (UserInfoMap::const_iterator iter = delegates_.begin();
+ iter != delegates_.end();
+ ++iter) {
+ if (iter->second.serial_number == serial_number)
+ return iter->second.delegate;
+ }
+ return NULL;
+}
+
+GCMClient::Delegate* UserList::GetDelegateByUsername(
+ const std::string& username) const {
+ UserInfoMap::const_iterator iter = delegates_.find(username);
+ return iter != delegates_.end() ? iter->second.delegate : NULL;
+}
+
+int64 UserList::GetSerialNumberForUsername(const std::string& username) const {
+ UserInfoMap::const_iterator iter = delegates_.find(username);
+ return iter != delegates_.end() ? iter->second.serial_number
+ : kInvalidSerialNumber;
+}
+
+} // namespace gcm
diff --git a/google_apis/gcm/engine/user_list.h b/google_apis/gcm/engine/user_list.h
new file mode 100644
index 0000000..e22215d
--- /dev/null
+++ b/google_apis/gcm/engine/user_list.h
@@ -0,0 +1,102 @@
+// 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.
+
+#ifndef GOOGLE_APIS_GCM_ENGINE_USER_LIST_H_
+#define GOOGLE_APIS_GCM_ENGINE_USER_LIST_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "google_apis/gcm/engine/gcm_store.h"
+#include "google_apis/gcm/gcm_client.h"
+
+namespace gcm {
+
+GCM_EXPORT extern const int64 kInvalidSerialNumber;
+
+// UserList stores mappings between usernames, serial numbers and delegates to
+// enable dispatching messages to applications.
+class GCM_EXPORT UserList {
+ public:
+ // A callback used by SetDelegate method to return a |user_serial_number|
+ // assigned to the delegate identified by |username|.
+ typedef base::Callback<void(const std::string& username,
+ int64 user_serial_number)> SetDelegateCallback;
+
+ explicit UserList(GCMStore* gcm_store);
+ ~UserList();
+
+ // Initializes the User List with a set of mappings and next serial number.
+ void Initialize(const GCMStore::SerialNumberMappings& result);
+
+ // Sets a user delegate for |username|. It will create a new entry for the
+ // user if one does not exist.
+ void SetDelegate(const std::string& username,
+ GCMClient::Delegate* delegate,
+ const SetDelegateCallback& callback);
+
+ // Returns a delegate for the user identified by |serial_number| or NULL, if
+ // a matching delegate was not found.
+ GCMClient::Delegate* GetDelegateBySerialNumber(int64 serial_number) const;
+
+ // Returns a delegate for the user identified by |username| or NULL, if a
+ // matching delegate was not found.
+ GCMClient::Delegate* GetDelegateByUsername(const std::string& username) const;
+
+ // Gets the serial number assigned to a specified |username|, if one is
+ // assigned, the value will be positive. If there is no matching delegate or
+ // it is not yet assigned a serial number, the result will be equal to
+ // kInvalidSerialNumber.
+ int64 GetSerialNumberForUsername(const std::string& username) const;
+
+ private:
+ friend class UserListTest;
+
+ struct UserInfo {
+ UserInfo();
+ explicit UserInfo(int64 serial_number);
+ UserInfo(GCMClient::Delegate* delegate,
+ const SetDelegateCallback& callback);
+ ~UserInfo();
+
+ int64 serial_number;
+ // Delegate related to the username. Not owned by the UserDelegate.
+ GCMClient::Delegate* delegate;
+ SetDelegateCallback callback;
+ };
+ typedef std::map<std::string, UserInfo> UserInfoMap;
+
+ // Assigns a serial number to the user identitified by |username|.
+ void AssignSerialNumber(const std::string& username);
+
+ // A callback invoked once the Backend is done updating the next serial
+ // number.
+ void IncrementSerialNumberCompleted(const std::string& username,
+ int64 user_serial_number,
+ bool success);
+
+ // Callback for serial number completion.
+ void AssignSerialNumberCompleted(const std::string& username, bool success);
+
+ // Concludes the process of setting a delegate by running a callback with
+ // |username| and |serial_number| assigned to that |username|. It will also
+ // reset the callback, so that it is not called again.
+ void OnSerialNumberReady(const UserInfoMap::iterator& iter);
+
+ // Sets the serial number related to the username. It expects the entry to not
+ // exist yet and will create it.
+ void SetSerialNumber(const std::string& username, int64 serial_number);
+
+ bool initialized_;
+ int64 next_serial_number_;
+ UserInfoMap delegates_;
+ GCMStore* gcm_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserList);
+};
+
+} // namespace gcm
+
+#endif // GOOGLE_APIS_GCM_ENGINE_USER_LIST_H_
diff --git a/google_apis/gcm/engine/user_list_unittest.cc b/google_apis/gcm/engine/user_list_unittest.cc
new file mode 100644
index 0000000..09947b9
--- /dev/null
+++ b/google_apis/gcm/engine/user_list_unittest.cc
@@ -0,0 +1,370 @@
+// 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 "google_apis/gcm/engine/user_list.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "google_apis/gcm/engine/gcm_store_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gcm {
+
+namespace {
+
+class GCMClientDelegate : public GCMClient::Delegate {
+ public:
+ explicit GCMClientDelegate(const std::string& username);
+ virtual ~GCMClientDelegate();
+
+ const std::string& GetUsername() const { return username_; }
+
+ // Overrides of GCMClientDelegate:
+ virtual void OnCheckInFinished(const GCMClient::CheckinInfo& checkin_info,
+ GCMClient::Result result) OVERRIDE {}
+ virtual void OnRegisterFinished(const std::string& app_id,
+ const std::string& registration_id,
+ GCMClient::Result result) OVERRIDE {}
+ virtual void OnSendFinished(const std::string& app_id,
+ const std::string& message_id,
+ GCMClient::Result result) OVERRIDE {}
+ virtual void OnMessageReceived(const std::string& app_id,
+ const GCMClient::IncomingMessage& message)
+ OVERRIDE {}
+ virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE {}
+ virtual void OnMessageSendError(const std::string& app_id,
+ const std::string& message_id,
+ GCMClient::Result result) OVERRIDE {}
+ virtual GCMClient::CheckinInfo GetCheckinInfo() const OVERRIDE {
+ return GCMClient::CheckinInfo();
+ }
+ virtual void OnLoadingCompleted() OVERRIDE {}
+ virtual base::TaskRunner* GetFileTaskRunner() OVERRIDE { return NULL; }
+
+ private:
+ std::string username_;
+};
+
+} // namespace
+
+class UserListTest : public testing::Test {
+ public:
+ UserListTest();
+ virtual ~UserListTest();
+
+ virtual void SetUp() OVERRIDE;
+
+ static size_t GetListSize(const UserList* user_list);
+ void SetDelegateCallback(const std::string& username,
+ int64 user_serial_number);
+
+ scoped_ptr<UserList> BuildUserList();
+
+ void PumpLoop();
+
+ void UpdateCallback(bool success);
+
+ void Initialize(UserList* user_list, const GCMStore::LoadResult& result);
+
+ void ResetLoop();
+
+ protected:
+ int64 last_assigned_serial_number_;
+ std::string last_assigned_username_;
+ scoped_ptr<GCMStore> gcm_store_;
+
+ private:
+ base::ScopedTempDir temp_directory_;
+ base::MessageLoop message_loop_;
+ scoped_ptr<base::RunLoop> run_loop_;
+};
+
+UserListTest::UserListTest()
+ : last_assigned_serial_number_(gcm::kInvalidSerialNumber) {}
+
+UserListTest::~UserListTest() {}
+
+void UserListTest::SetUp() {
+ ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
+}
+
+// static
+size_t UserListTest::GetListSize(const UserList* user_list) {
+ return user_list->delegates_.size();
+}
+
+void UserListTest::SetDelegateCallback(const std::string& username,
+ int64 user_serial_number) {
+ last_assigned_username_ = username;
+ last_assigned_serial_number_ = user_serial_number;
+ ResetLoop();
+}
+
+scoped_ptr<UserList> UserListTest::BuildUserList() {
+ gcm_store_.reset(new GCMStoreImpl(temp_directory_.path(),
+ message_loop_.message_loop_proxy()));
+ return scoped_ptr<UserList>(new UserList(gcm_store_.get()));
+}
+
+void UserListTest::Initialize(UserList* user_list,
+ const GCMStore::LoadResult& result) {
+ ASSERT_TRUE(result.success);
+ user_list->Initialize(result.serial_number_mappings);
+ ResetLoop();
+}
+
+void UserListTest::ResetLoop() {
+ if (run_loop_ && run_loop_->running())
+ run_loop_->Quit();
+}
+
+void UserListTest::PumpLoop() {
+ run_loop_.reset(new base::RunLoop);
+ run_loop_->Run();
+}
+
+void UserListTest::UpdateCallback(bool success) { ASSERT_TRUE(success); }
+
+GCMClientDelegate::GCMClientDelegate(const std::string& username)
+ : username_(username) {}
+
+GCMClientDelegate::~GCMClientDelegate() {}
+
+// Make sure it is possible to add a delegate, and that it is assigned a serial
+// number.
+TEST_F(UserListTest, SetDelegateAndCheckSerialNumberAssignment) {
+ scoped_ptr<UserList> user_list(BuildUserList());
+ gcm_store_->Load(base::Bind(
+ &UserListTest::Initialize, base::Unretained(this), user_list.get()));
+ PumpLoop();
+
+ // First adding a delegate.
+ scoped_ptr<GCMClientDelegate> delegate(new GCMClientDelegate("test_user_1"));
+ user_list->SetDelegate(
+ delegate->GetUsername(),
+ delegate.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ // Verify the record was created.
+ EXPECT_EQ(1u, GetListSize(user_list.get()));
+ // Verify username and serial number were assigned.
+ EXPECT_EQ(delegate->GetUsername(), last_assigned_username_);
+ EXPECT_NE(gcm::kInvalidSerialNumber, last_assigned_serial_number_);
+ // Check that a serial number was assigned to delegate.
+ EXPECT_EQ(last_assigned_serial_number_,
+ user_list->GetSerialNumberForUsername(delegate->GetUsername()));
+}
+
+// Get the delegate that was added to the list by both serial number and
+// username.
+TEST_F(UserListTest, GetDelegate) {
+ scoped_ptr<UserList> user_list(BuildUserList());
+ gcm_store_->Load(base::Bind(
+ &UserListTest::Initialize, base::Unretained(this), user_list.get()));
+ PumpLoop();
+
+ // Start by adding a delegate and a serial number.
+ scoped_ptr<GCMClientDelegate> delegate(new GCMClientDelegate("test_user_1"));
+ user_list->SetDelegate(
+ delegate->GetUsername(),
+ delegate.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ EXPECT_EQ(delegate.get(),
+ user_list->GetDelegateBySerialNumber(last_assigned_serial_number_));
+ EXPECT_EQ(delegate.get(),
+ user_list->GetDelegateByUsername(last_assigned_username_));
+}
+
+// Make sure that correct mapping between username of the delegate and a serial
+// number is preserved when loading the user list. Also verify that setting a
+// delegate after load works correctly. (Finds the existing mapping entry.)
+TEST_F(UserListTest, LoadUserEntrySetDelegate) {
+ scoped_ptr<UserList> user_list(BuildUserList());
+ gcm_store_->Load(base::Bind(
+ &UserListTest::Initialize, base::Unretained(this), user_list.get()));
+ PumpLoop();
+
+ // Start by adding a delegate and a serial number.
+ scoped_ptr<GCMClientDelegate> delegate(new GCMClientDelegate("test_user_1"));
+ user_list->SetDelegate(
+ delegate->GetUsername(),
+ delegate.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ // Reload the GCM User List.
+ user_list = BuildUserList().Pass();
+ gcm_store_->Load(base::Bind(
+ &UserListTest::Initialize, base::Unretained(this), user_list.get()));
+ PumpLoop();
+
+ // Verify a single record was loaded, with matching username, but no delegate.
+ EXPECT_EQ(1u, GetListSize(user_list.get()));
+ int64 serial_number =
+ user_list->GetSerialNumberForUsername(delegate->GetUsername());
+ EXPECT_EQ(last_assigned_serial_number_, serial_number);
+ EXPECT_EQ(NULL, user_list->GetDelegateBySerialNumber(serial_number));
+ EXPECT_EQ(NULL, user_list->GetDelegateByUsername(delegate->GetUsername()));
+
+ // After loading is complete, Delegates will start adding itself looking for
+ // their serial numbers. Check that correct matches are found and new records
+ // not created.
+ user_list->SetDelegate(
+ delegate->GetUsername(),
+ delegate.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ EXPECT_EQ(1u, GetListSize(user_list.get()));
+ serial_number =
+ user_list->GetSerialNumberForUsername(delegate->GetUsername());
+ EXPECT_EQ(last_assigned_serial_number_, serial_number);
+ EXPECT_EQ(delegate.get(),
+ user_list->GetDelegateBySerialNumber(serial_number));
+ EXPECT_EQ(delegate.get(),
+ user_list->GetDelegateByUsername(delegate->GetUsername()));
+}
+
+// Check that it is possible to add multiple delegates to the user list.
+TEST_F(UserListTest, AddMultipleDelegates) {
+ scoped_ptr<UserList> user_list(BuildUserList());
+ gcm_store_->Load(base::Bind(
+ &UserListTest::Initialize, base::Unretained(this), user_list.get()));
+ PumpLoop();
+
+ // Start by adding a delegate and a serial number.
+ scoped_ptr<GCMClientDelegate> delegate1(new GCMClientDelegate("test_user_1"));
+ user_list->SetDelegate(
+ delegate1->GetUsername(),
+ delegate1.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ int64 serial_number1 = last_assigned_serial_number_;
+
+ scoped_ptr<GCMClientDelegate> delegate2(new GCMClientDelegate("test_user_2"));
+ user_list->SetDelegate(
+ delegate2->GetUsername(),
+ delegate2.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ int64 serial_number2 = last_assigned_serial_number_;
+
+ // Ensuring that serial numbers are different.
+ EXPECT_EQ(2u, GetListSize(user_list.get()));
+
+ // Reading the user entries.
+ user_list = BuildUserList().Pass();
+ gcm_store_->Load(base::Bind(
+ &UserListTest::Initialize, base::Unretained(this), user_list.get()));
+ PumpLoop();
+
+ // Serial numbers stay the same, but there are no delegates assigned.
+ EXPECT_EQ(2u, GetListSize(user_list.get()));
+ EXPECT_EQ(NULL, user_list->GetDelegateByUsername(delegate1->GetUsername()));
+ EXPECT_EQ(NULL, user_list->GetDelegateBySerialNumber(serial_number1));
+ EXPECT_EQ(NULL, user_list->GetDelegateByUsername(delegate2->GetUsername()));
+ EXPECT_EQ(NULL, user_list->GetDelegateBySerialNumber(serial_number2));
+
+ // Setting and checking a delegate on the second user.
+ user_list->SetDelegate(
+ delegate2->GetUsername(),
+ delegate2.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ // First user still does not have a delegate.
+ EXPECT_EQ(NULL, user_list->GetDelegateByUsername(delegate1->GetUsername()));
+ EXPECT_EQ(NULL, user_list->GetDelegateBySerialNumber(serial_number1));
+ // Second user has a delegate set.
+ EXPECT_EQ(delegate2.get(),
+ user_list->GetDelegateByUsername(delegate2->GetUsername()));
+ EXPECT_EQ(delegate2.get(),
+ user_list->GetDelegateBySerialNumber(serial_number2));
+}
+
+// Adding a delegate before the user list is initialized. Verifies that serial
+// number assignment is postponed until after initialization.
+TEST_F(UserListTest, AddDelegateThenInitializeWithoutSerialNumber) {
+ scoped_ptr<UserList> user_list(BuildUserList());
+
+ // Add a delegate first.
+ scoped_ptr<GCMClientDelegate> delegate(new GCMClientDelegate("test_user_1"));
+ user_list->SetDelegate(
+ delegate->GetUsername(),
+ delegate.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+
+ EXPECT_EQ(gcm::kInvalidSerialNumber, last_assigned_serial_number_);
+ EXPECT_EQ("", last_assigned_username_);
+
+ // Now run the initialization.
+ gcm_store_->Load(base::Bind(
+ &UserListTest::Initialize, base::Unretained(this), user_list.get()));
+ // Need to pump twice, due to initialization triggering additional set of
+ // callbacks to be run.
+ PumpLoop();
+ PumpLoop();
+
+ EXPECT_EQ(last_assigned_serial_number_,
+ user_list->GetSerialNumberForUsername(delegate->GetUsername()));
+ EXPECT_EQ(delegate->GetUsername(), last_assigned_username_);
+}
+
+// Adding a delegate that already has a serial number on a subsequent restart of
+// the user list and prior to initialization. Expects to assing correct serial
+// number based on existing mappings.
+TEST_F(UserListTest, AddDelegateThenInitializeWithSerialNumber) {
+ scoped_ptr<UserList> user_list(BuildUserList());
+ gcm_store_->Load(base::Bind(
+ &UserListTest::Initialize, base::Unretained(this), user_list.get()));
+ PumpLoop();
+
+ // First add a delegate to the list so that serial number is persisted.
+ scoped_ptr<GCMClientDelegate> delegate(new GCMClientDelegate("test_user_1"));
+ user_list->SetDelegate(
+ delegate->GetUsername(),
+ delegate.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ last_assigned_serial_number_ = gcm::kInvalidSerialNumber;
+ last_assigned_username_ = "";
+
+ // Resetting the user list to make sure it is not initialized.
+ user_list = BuildUserList().Pass();
+
+ // Add a delegate again, this time no callback is expected until the list is
+ // initialized.
+ user_list->SetDelegate(
+ delegate->GetUsername(),
+ delegate.get(),
+ base::Bind(&UserListTest::SetDelegateCallback, base::Unretained(this)));
+
+ // Now run the initialization.
+ gcm_store_->Load(base::Bind(
+ &UserListTest::Initialize, base::Unretained(this), user_list.get()));
+ // Need to pump twice, due to initialization triggering additional set of
+ // callbacks to be run.
+ PumpLoop();
+ PumpLoop();
+
+ EXPECT_EQ(last_assigned_serial_number_,
+ user_list->GetSerialNumberForUsername(delegate->GetUsername()));
+ EXPECT_EQ(delegate->GetUsername(), last_assigned_username_);
+}
+
+} // namespace gcm
diff --git a/google_apis/gcm/gcm.gyp b/google_apis/gcm/gcm.gyp
index ad4989a..f1727e0 100644
--- a/google_apis/gcm/gcm.gyp
+++ b/google_apis/gcm/gcm.gyp
@@ -64,6 +64,8 @@
'engine/mcs_client.h',
'engine/registration_request.cc',
'engine/registration_request.h',
+ 'engine/user_list.cc',
+ 'engine/user_list.h',
'gcm_client.cc',
'gcm_client.h',
'gcm_client_impl.cc',
@@ -133,6 +135,7 @@
'engine/heartbeat_manager_unittest.cc',
'engine/mcs_client_unittest.cc',
'engine/registration_request_unittest.cc',
+ 'engine/user_list_unittest.cc',
]
},
],
diff --git a/google_apis/gcm/gcm_client_impl.cc b/google_apis/gcm/gcm_client_impl.cc
index 86da9d5..8d09050 100644
--- a/google_apis/gcm/gcm_client_impl.cc
+++ b/google_apis/gcm/gcm_client_impl.cc
@@ -4,6 +4,12 @@
#include "google_apis/gcm/gcm_client_impl.h"
+#include "base/files/file_path.h"
+#include "base/sequenced_task_runner.h"
+#include "google_apis/gcm/engine/gcm_store.h"
+#include "google_apis/gcm/engine/gcm_store_impl.h"
+#include "google_apis/gcm/engine/user_list.h"
+
namespace gcm {
GCMClientImpl::GCMClientImpl() {
@@ -12,6 +18,13 @@ GCMClientImpl::GCMClientImpl() {
GCMClientImpl::~GCMClientImpl() {
}
+void GCMClientImpl::Initialize(
+ const base::FilePath& path,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
+ gcm_store_.reset(new GCMStoreImpl(path, blocking_task_runner));
+ user_list_.reset(new UserList(gcm_store_.get()));
+}
+
void GCMClientImpl::SetUserDelegate(const std::string& username,
Delegate* delegate) {
}
diff --git a/google_apis/gcm/gcm_client_impl.h b/google_apis/gcm/gcm_client_impl.h
index 797c325..475de85 100644
--- a/google_apis/gcm/gcm_client_impl.h
+++ b/google_apis/gcm/gcm_client_impl.h
@@ -6,15 +6,29 @@
#define GOOGLE_APIS_GCM_GCM_CLIENT_IMPL_H_
#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "google_apis/gcm/gcm_client.h"
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+} // namespace base
+
namespace gcm {
+class GCMStore;
+class UserList;
+
class GCMClientImpl : public GCMClient {
public:
GCMClientImpl();
virtual ~GCMClientImpl();
+ void Initialize(
+ const base::FilePath& path,
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
+
// Overridden from GCMClient:
virtual void SetUserDelegate(const std::string& username,
Delegate* delegate) OVERRIDE;
@@ -32,6 +46,9 @@ class GCMClientImpl : public GCMClient {
virtual bool IsLoading() const OVERRIDE;
private:
+ scoped_ptr<GCMStore> gcm_store_;
+ scoped_ptr<UserList> user_list_;
+
DISALLOW_COPY_AND_ASSIGN(GCMClientImpl);
};