summaryrefslogtreecommitdiffstats
path: root/chromium/google_apis/gcm/engine/gcm_store_impl_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/google_apis/gcm/engine/gcm_store_impl_unittest.cc')
-rw-r--r--chromium/google_apis/gcm/engine/gcm_store_impl_unittest.cc552
1 files changed, 552 insertions, 0 deletions
diff --git a/chromium/google_apis/gcm/engine/gcm_store_impl_unittest.cc b/chromium/google_apis/gcm/engine/gcm_store_impl_unittest.cc
new file mode 100644
index 00000000000..7b9c8936529
--- /dev/null
+++ b/chromium/google_apis/gcm/engine/gcm_store_impl_unittest.cc
@@ -0,0 +1,552 @@
+// 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/gcm_store_impl.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/command_line.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 "base/strings/string_number_conversions.h"
+#include "google_apis/gcm/base/fake_encryptor.h"
+#include "google_apis/gcm/base/mcs_message.h"
+#include "google_apis/gcm/base/mcs_util.h"
+#include "google_apis/gcm/protocol/mcs.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gcm {
+
+namespace {
+
+// Number of persistent ids to use in tests.
+const int kNumPersistentIds = 10;
+
+// Number of per-app messages in tests.
+const int kNumMessagesPerApp = 20;
+
+// App name for testing.
+const char kAppName[] = "my_app";
+
+// Category name for testing.
+const char kCategoryName[] = "my_category";
+
+const uint64 kDeviceId = 22;
+const uint64 kDeviceToken = 55;
+
+class GCMStoreImplTest : public testing::Test {
+ public:
+ GCMStoreImplTest();
+ virtual ~GCMStoreImplTest();
+
+ virtual void SetUp() OVERRIDE;
+
+ scoped_ptr<GCMStore> BuildGCMStore();
+
+ std::string GetNextPersistentId();
+
+ void PumpLoop();
+
+ void LoadCallback(scoped_ptr<GCMStore::LoadResult>* result_dst,
+ scoped_ptr<GCMStore::LoadResult> result);
+ void UpdateCallback(bool success);
+
+ protected:
+ base::MessageLoop message_loop_;
+ base::ScopedTempDir temp_directory_;
+ bool expected_success_;
+ uint64 next_persistent_id_;
+ scoped_ptr<base::RunLoop> run_loop_;
+};
+
+GCMStoreImplTest::GCMStoreImplTest()
+ : expected_success_(true),
+ next_persistent_id_(base::Time::Now().ToInternalValue()) {
+ EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
+ run_loop_.reset(new base::RunLoop());
+}
+
+GCMStoreImplTest::~GCMStoreImplTest() {}
+
+void GCMStoreImplTest::SetUp() {
+ testing::Test::SetUp();
+}
+
+scoped_ptr<GCMStore> GCMStoreImplTest::BuildGCMStore() {
+ return scoped_ptr<GCMStore>(new GCMStoreImpl(
+ temp_directory_.path(),
+ message_loop_.message_loop_proxy(),
+ make_scoped_ptr<Encryptor>(new FakeEncryptor)));
+}
+
+std::string GCMStoreImplTest::GetNextPersistentId() {
+ return base::Uint64ToString(next_persistent_id_++);
+}
+
+void GCMStoreImplTest::PumpLoop() { message_loop_.RunUntilIdle(); }
+
+void GCMStoreImplTest::LoadCallback(
+ scoped_ptr<GCMStore::LoadResult>* result_dst,
+ scoped_ptr<GCMStore::LoadResult> result) {
+ ASSERT_TRUE(result->success);
+ *result_dst = result.Pass();
+ run_loop_->Quit();
+ run_loop_.reset(new base::RunLoop());
+}
+
+void GCMStoreImplTest::UpdateCallback(bool success) {
+ ASSERT_EQ(expected_success_, success);
+}
+
+// Verify creating a new database and loading it.
+TEST_F(GCMStoreImplTest, LoadNew) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ EXPECT_EQ(0U, load_result->device_android_id);
+ EXPECT_EQ(0U, load_result->device_security_token);
+ EXPECT_TRUE(load_result->incoming_messages.empty());
+ EXPECT_TRUE(load_result->outgoing_messages.empty());
+ EXPECT_TRUE(load_result->gservices_settings.empty());
+ EXPECT_EQ(base::Time::FromInternalValue(0LL), load_result->last_checkin_time);
+}
+
+TEST_F(GCMStoreImplTest, DeviceCredentials) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ gcm_store->SetDeviceCredentials(
+ kDeviceId,
+ kDeviceToken,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_EQ(kDeviceId, load_result->device_android_id);
+ ASSERT_EQ(kDeviceToken, load_result->device_security_token);
+}
+
+TEST_F(GCMStoreImplTest, LastCheckinTime) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ base::Time last_checkin_time = base::Time::Now();
+
+ gcm_store->SetLastCheckinTime(
+ last_checkin_time,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+ ASSERT_EQ(last_checkin_time, load_result->last_checkin_time);
+}
+
+TEST_F(GCMStoreImplTest, GServicesSettings_ProtocolV2) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ std::map<std::string, std::string> settings;
+ settings["checkin_interval"] = "12345";
+ settings["mcs_port"] = "438";
+ settings["checkin_url"] = "http://checkin.google.com";
+ std::string digest = "digest1";
+
+ gcm_store->SetGServicesSettings(
+ settings,
+ digest,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_EQ(settings, load_result->gservices_settings);
+ ASSERT_EQ(digest, load_result->gservices_digest);
+
+ // Remove some, and add some.
+ settings.clear();
+ settings["checkin_interval"] = "54321";
+ settings["registration_url"] = "http://registration.google.com";
+ digest = "digest2";
+
+ gcm_store->SetGServicesSettings(
+ settings,
+ digest,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_EQ(settings, load_result->gservices_settings);
+ ASSERT_EQ(digest, load_result->gservices_digest);
+}
+
+TEST_F(GCMStoreImplTest, Registrations) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ // Add one registration with one sender.
+ linked_ptr<RegistrationInfo> registration1(new RegistrationInfo);
+ registration1->sender_ids.push_back("sender1");
+ registration1->registration_id = "registration1";
+ gcm_store->AddRegistration(
+ "app1",
+ registration1,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ // Add one registration with multiple senders.
+ linked_ptr<RegistrationInfo> registration2(new RegistrationInfo);
+ registration2->sender_ids.push_back("sender2_1");
+ registration2->sender_ids.push_back("sender2_2");
+ registration2->registration_id = "registration2";
+ gcm_store->AddRegistration(
+ "app2",
+ registration2,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_EQ(2u, load_result->registrations.size());
+ ASSERT_TRUE(load_result->registrations.find("app1") !=
+ load_result->registrations.end());
+ EXPECT_EQ(registration1->registration_id,
+ load_result->registrations["app1"]->registration_id);
+ ASSERT_EQ(1u, load_result->registrations["app1"]->sender_ids.size());
+ EXPECT_EQ(registration1->sender_ids[0],
+ load_result->registrations["app1"]->sender_ids[0]);
+ ASSERT_TRUE(load_result->registrations.find("app2") !=
+ load_result->registrations.end());
+ EXPECT_EQ(registration2->registration_id,
+ load_result->registrations["app2"]->registration_id);
+ ASSERT_EQ(2u, load_result->registrations["app2"]->sender_ids.size());
+ EXPECT_EQ(registration2->sender_ids[0],
+ load_result->registrations["app2"]->sender_ids[0]);
+ EXPECT_EQ(registration2->sender_ids[1],
+ load_result->registrations["app2"]->sender_ids[1]);
+}
+
+// Verify saving some incoming messages, reopening the directory, and then
+// removing those incoming messages.
+TEST_F(GCMStoreImplTest, IncomingMessages) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ std::vector<std::string> persistent_ids;
+ for (int i = 0; i < kNumPersistentIds; ++i) {
+ persistent_ids.push_back(GetNextPersistentId());
+ gcm_store->AddIncomingMessage(
+ persistent_ids.back(),
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+ }
+
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_EQ(persistent_ids, load_result->incoming_messages);
+ ASSERT_TRUE(load_result->outgoing_messages.empty());
+
+ gcm_store->RemoveIncomingMessages(
+ persistent_ids,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ gcm_store = BuildGCMStore().Pass();
+ load_result->incoming_messages.clear();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_TRUE(load_result->incoming_messages.empty());
+ ASSERT_TRUE(load_result->outgoing_messages.empty());
+}
+
+// Verify saving some outgoing messages, reopening the directory, and then
+// removing those outgoing messages.
+TEST_F(GCMStoreImplTest, OutgoingMessages) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ std::vector<std::string> persistent_ids;
+ const int kNumPersistentIds = 10;
+ for (int i = 0; i < kNumPersistentIds; ++i) {
+ persistent_ids.push_back(GetNextPersistentId());
+ mcs_proto::DataMessageStanza message;
+ message.set_from(kAppName + persistent_ids.back());
+ message.set_category(kCategoryName + persistent_ids.back());
+ gcm_store->AddOutgoingMessage(
+ persistent_ids.back(),
+ MCSMessage(message),
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+ }
+
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_TRUE(load_result->incoming_messages.empty());
+ ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
+ for (int i = 0; i < kNumPersistentIds; ++i) {
+ std::string id = persistent_ids[i];
+ ASSERT_TRUE(load_result->outgoing_messages[id].get());
+ const mcs_proto::DataMessageStanza* message =
+ reinterpret_cast<mcs_proto::DataMessageStanza*>(
+ load_result->outgoing_messages[id].get());
+ ASSERT_EQ(message->from(), kAppName + id);
+ ASSERT_EQ(message->category(), kCategoryName + id);
+ }
+
+ gcm_store->RemoveOutgoingMessages(
+ persistent_ids,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ gcm_store = BuildGCMStore().Pass();
+ load_result->outgoing_messages.clear();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_TRUE(load_result->incoming_messages.empty());
+ ASSERT_TRUE(load_result->outgoing_messages.empty());
+}
+
+// Verify incoming and outgoing messages don't conflict.
+TEST_F(GCMStoreImplTest, IncomingAndOutgoingMessages) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ std::vector<std::string> persistent_ids;
+ const int kNumPersistentIds = 10;
+ for (int i = 0; i < kNumPersistentIds; ++i) {
+ persistent_ids.push_back(GetNextPersistentId());
+ gcm_store->AddIncomingMessage(
+ persistent_ids.back(),
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ mcs_proto::DataMessageStanza message;
+ message.set_from(kAppName + persistent_ids.back());
+ message.set_category(kCategoryName + persistent_ids.back());
+ gcm_store->AddOutgoingMessage(
+ persistent_ids.back(),
+ MCSMessage(message),
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+ }
+
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_EQ(persistent_ids, load_result->incoming_messages);
+ ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
+ for (int i = 0; i < kNumPersistentIds; ++i) {
+ std::string id = persistent_ids[i];
+ ASSERT_TRUE(load_result->outgoing_messages[id].get());
+ const mcs_proto::DataMessageStanza* message =
+ reinterpret_cast<mcs_proto::DataMessageStanza*>(
+ load_result->outgoing_messages[id].get());
+ ASSERT_EQ(message->from(), kAppName + id);
+ ASSERT_EQ(message->category(), kCategoryName + id);
+ }
+
+ gcm_store->RemoveIncomingMessages(
+ persistent_ids,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+ gcm_store->RemoveOutgoingMessages(
+ persistent_ids,
+ base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
+ PumpLoop();
+
+ gcm_store = BuildGCMStore().Pass();
+ load_result->incoming_messages.clear();
+ load_result->outgoing_messages.clear();
+ gcm_store->Load(base::Bind(
+ &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
+ PumpLoop();
+
+ ASSERT_TRUE(load_result->incoming_messages.empty());
+ ASSERT_TRUE(load_result->outgoing_messages.empty());
+}
+
+// Test that per-app message limits are enforced, persisted across restarts,
+// and updated as messages are removed.
+TEST_F(GCMStoreImplTest, PerAppMessageLimits) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
+ base::Unretained(this),
+ &load_result));
+
+ // Add the initial (below app limit) messages.
+ for (int i = 0; i < kNumMessagesPerApp; ++i) {
+ mcs_proto::DataMessageStanza message;
+ message.set_from(kAppName);
+ message.set_category(kCategoryName);
+ EXPECT_TRUE(gcm_store->AddOutgoingMessage(
+ base::IntToString(i),
+ MCSMessage(message),
+ base::Bind(&GCMStoreImplTest::UpdateCallback,
+ base::Unretained(this))));
+ PumpLoop();
+ }
+
+ // Attempting to add some more should fail.
+ for (int i = 0; i < kNumMessagesPerApp; ++i) {
+ mcs_proto::DataMessageStanza message;
+ message.set_from(kAppName);
+ message.set_category(kCategoryName);
+ EXPECT_FALSE(gcm_store->AddOutgoingMessage(
+ base::IntToString(i + kNumMessagesPerApp),
+ MCSMessage(message),
+ base::Bind(&GCMStoreImplTest::UpdateCallback,
+ base::Unretained(this))));
+ PumpLoop();
+ }
+
+ // Tear down and restore the database.
+ gcm_store = BuildGCMStore().Pass();
+ gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
+ base::Unretained(this),
+ &load_result));
+ PumpLoop();
+
+ // Adding more messages should still fail.
+ for (int i = 0; i < kNumMessagesPerApp; ++i) {
+ mcs_proto::DataMessageStanza message;
+ message.set_from(kAppName);
+ message.set_category(kCategoryName);
+ EXPECT_FALSE(gcm_store->AddOutgoingMessage(
+ base::IntToString(i + kNumMessagesPerApp),
+ MCSMessage(message),
+ base::Bind(&GCMStoreImplTest::UpdateCallback,
+ base::Unretained(this))));
+ PumpLoop();
+ }
+
+ // Remove the existing messages.
+ for (int i = 0; i < kNumMessagesPerApp; ++i) {
+ gcm_store->RemoveOutgoingMessage(
+ base::IntToString(i),
+ base::Bind(&GCMStoreImplTest::UpdateCallback,
+ base::Unretained(this)));
+ PumpLoop();
+ }
+
+ // Successfully add new messages.
+ for (int i = 0; i < kNumMessagesPerApp; ++i) {
+ mcs_proto::DataMessageStanza message;
+ message.set_from(kAppName);
+ message.set_category(kCategoryName);
+ EXPECT_TRUE(gcm_store->AddOutgoingMessage(
+ base::IntToString(i + kNumMessagesPerApp),
+ MCSMessage(message),
+ base::Bind(&GCMStoreImplTest::UpdateCallback,
+ base::Unretained(this))));
+ PumpLoop();
+ }
+}
+
+// When the database is destroyed, all database updates should fail. At the
+// same time, they per-app message counts should not go up, as failures should
+// result in decrementing the counts.
+TEST_F(GCMStoreImplTest, AddMessageAfterDestroy) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
+ base::Unretained(this),
+ &load_result));
+ PumpLoop();
+ gcm_store->Destroy(base::Bind(&GCMStoreImplTest::UpdateCallback,
+ base::Unretained(this)));
+ PumpLoop();
+
+ expected_success_ = false;
+ for (int i = 0; i < kNumMessagesPerApp * 2; ++i) {
+ mcs_proto::DataMessageStanza message;
+ message.set_from(kAppName);
+ message.set_category(kCategoryName);
+ // Because all adds are failing, none should hit the per-app message limits.
+ EXPECT_TRUE(gcm_store->AddOutgoingMessage(
+ base::IntToString(i),
+ MCSMessage(message),
+ base::Bind(&GCMStoreImplTest::UpdateCallback,
+ base::Unretained(this))));
+ PumpLoop();
+ }
+}
+
+TEST_F(GCMStoreImplTest, ReloadAfterClose) {
+ scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
+ scoped_ptr<GCMStore::LoadResult> load_result;
+ gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
+ base::Unretained(this),
+ &load_result));
+ PumpLoop();
+
+ gcm_store->Close();
+ PumpLoop();
+
+ gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
+ base::Unretained(this),
+ &load_result));
+ PumpLoop();
+}
+
+} // namespace
+
+} // namespace gcm