1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "google_apis/gcm/engine/gcm_store_impl.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/run_loop.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "components/os_crypt/os_crypt_switches.h"
19 #include "google_apis/gcm/base/mcs_message.h"
20 #include "google_apis/gcm/base/mcs_util.h"
21 #include "google_apis/gcm/protocol/mcs.pb.h"
22 #include "testing/gtest/include/gtest/gtest.h"
28 // Number of persistent ids to use in tests.
29 const int kNumPersistentIds = 10;
31 // Number of per-app messages in tests.
32 const int kNumMessagesPerApp = 20;
34 // App name for testing.
35 const char kAppName[] = "my_app";
37 // Category name for testing.
38 const char kCategoryName[] = "my_category";
40 const uint64 kDeviceId = 22;
41 const uint64 kDeviceToken = 55;
43 class GCMStoreImplTest : public testing::Test {
46 virtual ~GCMStoreImplTest();
48 virtual void SetUp() OVERRIDE;
50 scoped_ptr<GCMStore> BuildGCMStore();
52 std::string GetNextPersistentId();
56 void LoadCallback(scoped_ptr<GCMStore::LoadResult>* result_dst,
57 scoped_ptr<GCMStore::LoadResult> result);
58 void UpdateCallback(bool success);
61 base::MessageLoop message_loop_;
62 base::ScopedTempDir temp_directory_;
63 bool expected_success_;
64 uint64 next_persistent_id_;
65 scoped_ptr<base::RunLoop> run_loop_;
68 GCMStoreImplTest::GCMStoreImplTest()
69 : expected_success_(true),
70 next_persistent_id_(base::Time::Now().ToInternalValue()) {
71 EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
72 run_loop_.reset(new base::RunLoop());
75 GCMStoreImplTest::~GCMStoreImplTest() {}
77 void GCMStoreImplTest::SetUp() {
78 testing::Test::SetUp();
79 #if defined(OS_MACOSX)
80 base::CommandLine::ForCurrentProcess()->AppendSwitch(
81 os_crypt::switches::kUseMockKeychain);
85 scoped_ptr<GCMStore> GCMStoreImplTest::BuildGCMStore() {
86 return scoped_ptr<GCMStore>(new GCMStoreImpl(
87 temp_directory_.path(),
88 message_loop_.message_loop_proxy()));
91 std::string GCMStoreImplTest::GetNextPersistentId() {
92 return base::Uint64ToString(next_persistent_id_++);
95 void GCMStoreImplTest::PumpLoop() { message_loop_.RunUntilIdle(); }
97 void GCMStoreImplTest::LoadCallback(
98 scoped_ptr<GCMStore::LoadResult>* result_dst,
99 scoped_ptr<GCMStore::LoadResult> result) {
100 ASSERT_TRUE(result->success);
101 *result_dst = result.Pass();
103 run_loop_.reset(new base::RunLoop());
106 void GCMStoreImplTest::UpdateCallback(bool success) {
107 ASSERT_EQ(expected_success_, success);
110 // Verify creating a new database and loading it.
111 TEST_F(GCMStoreImplTest, LoadNew) {
112 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
113 scoped_ptr<GCMStore::LoadResult> load_result;
114 gcm_store->Load(base::Bind(
115 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
118 EXPECT_EQ(0U, load_result->device_android_id);
119 EXPECT_EQ(0U, load_result->device_security_token);
120 EXPECT_TRUE(load_result->incoming_messages.empty());
121 EXPECT_TRUE(load_result->outgoing_messages.empty());
122 EXPECT_TRUE(load_result->gservices_settings.empty());
123 EXPECT_EQ(base::Time::FromInternalValue(0LL), load_result->last_checkin_time);
126 TEST_F(GCMStoreImplTest, DeviceCredentials) {
127 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
128 scoped_ptr<GCMStore::LoadResult> load_result;
129 gcm_store->Load(base::Bind(
130 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
133 gcm_store->SetDeviceCredentials(
136 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
139 gcm_store = BuildGCMStore().Pass();
140 gcm_store->Load(base::Bind(
141 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
144 ASSERT_EQ(kDeviceId, load_result->device_android_id);
145 ASSERT_EQ(kDeviceToken, load_result->device_security_token);
148 TEST_F(GCMStoreImplTest, LastCheckinTime) {
149 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
150 scoped_ptr<GCMStore::LoadResult> load_result;
151 gcm_store->Load(base::Bind(
152 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
155 base::Time last_checkin_time = base::Time::Now();
157 gcm_store->SetLastCheckinTime(
159 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
162 gcm_store = BuildGCMStore().Pass();
163 gcm_store->Load(base::Bind(
164 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
166 ASSERT_EQ(last_checkin_time, load_result->last_checkin_time);
169 TEST_F(GCMStoreImplTest, GServicesSettings_ProtocolV2) {
170 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
171 scoped_ptr<GCMStore::LoadResult> load_result;
172 gcm_store->Load(base::Bind(
173 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
176 std::map<std::string, std::string> settings;
177 settings["checkin_interval"] = "12345";
178 settings["mcs_port"] = "438";
179 settings["checkin_url"] = "http://checkin.google.com";
180 std::string digest = "digest1";
182 gcm_store->SetGServicesSettings(
185 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
188 gcm_store = BuildGCMStore().Pass();
189 gcm_store->Load(base::Bind(
190 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
193 ASSERT_EQ(settings, load_result->gservices_settings);
194 ASSERT_EQ(digest, load_result->gservices_digest);
196 // Remove some, and add some.
198 settings["checkin_interval"] = "54321";
199 settings["registration_url"] = "http://registration.google.com";
202 gcm_store->SetGServicesSettings(
205 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
208 gcm_store = BuildGCMStore().Pass();
209 gcm_store->Load(base::Bind(
210 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
213 ASSERT_EQ(settings, load_result->gservices_settings);
214 ASSERT_EQ(digest, load_result->gservices_digest);
217 TEST_F(GCMStoreImplTest, Registrations) {
218 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
219 scoped_ptr<GCMStore::LoadResult> load_result;
220 gcm_store->Load(base::Bind(
221 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
224 // Add one registration with one sender.
225 linked_ptr<RegistrationInfo> registration1(new RegistrationInfo);
226 registration1->sender_ids.push_back("sender1");
227 registration1->registration_id = "registration1";
228 gcm_store->AddRegistration(
231 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
234 // Add one registration with multiple senders.
235 linked_ptr<RegistrationInfo> registration2(new RegistrationInfo);
236 registration2->sender_ids.push_back("sender2_1");
237 registration2->sender_ids.push_back("sender2_2");
238 registration2->registration_id = "registration2";
239 gcm_store->AddRegistration(
242 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
245 gcm_store = BuildGCMStore().Pass();
246 gcm_store->Load(base::Bind(
247 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
250 ASSERT_EQ(2u, load_result->registrations.size());
251 ASSERT_TRUE(load_result->registrations.find("app1") !=
252 load_result->registrations.end());
253 EXPECT_EQ(registration1->registration_id,
254 load_result->registrations["app1"]->registration_id);
255 ASSERT_EQ(1u, load_result->registrations["app1"]->sender_ids.size());
256 EXPECT_EQ(registration1->sender_ids[0],
257 load_result->registrations["app1"]->sender_ids[0]);
258 ASSERT_TRUE(load_result->registrations.find("app2") !=
259 load_result->registrations.end());
260 EXPECT_EQ(registration2->registration_id,
261 load_result->registrations["app2"]->registration_id);
262 ASSERT_EQ(2u, load_result->registrations["app2"]->sender_ids.size());
263 EXPECT_EQ(registration2->sender_ids[0],
264 load_result->registrations["app2"]->sender_ids[0]);
265 EXPECT_EQ(registration2->sender_ids[1],
266 load_result->registrations["app2"]->sender_ids[1]);
269 // Verify saving some incoming messages, reopening the directory, and then
270 // removing those incoming messages.
271 TEST_F(GCMStoreImplTest, IncomingMessages) {
272 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
273 scoped_ptr<GCMStore::LoadResult> load_result;
274 gcm_store->Load(base::Bind(
275 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
278 std::vector<std::string> persistent_ids;
279 for (int i = 0; i < kNumPersistentIds; ++i) {
280 persistent_ids.push_back(GetNextPersistentId());
281 gcm_store->AddIncomingMessage(
282 persistent_ids.back(),
283 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
287 gcm_store = BuildGCMStore().Pass();
288 gcm_store->Load(base::Bind(
289 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
292 ASSERT_EQ(persistent_ids, load_result->incoming_messages);
293 ASSERT_TRUE(load_result->outgoing_messages.empty());
295 gcm_store->RemoveIncomingMessages(
297 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
300 gcm_store = BuildGCMStore().Pass();
301 load_result->incoming_messages.clear();
302 gcm_store->Load(base::Bind(
303 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
306 ASSERT_TRUE(load_result->incoming_messages.empty());
307 ASSERT_TRUE(load_result->outgoing_messages.empty());
310 // Verify saving some outgoing messages, reopening the directory, and then
311 // removing those outgoing messages.
312 TEST_F(GCMStoreImplTest, OutgoingMessages) {
313 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
314 scoped_ptr<GCMStore::LoadResult> load_result;
315 gcm_store->Load(base::Bind(
316 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
319 std::vector<std::string> persistent_ids;
320 const int kNumPersistentIds = 10;
321 for (int i = 0; i < kNumPersistentIds; ++i) {
322 persistent_ids.push_back(GetNextPersistentId());
323 mcs_proto::DataMessageStanza message;
324 message.set_from(kAppName + persistent_ids.back());
325 message.set_category(kCategoryName + persistent_ids.back());
326 gcm_store->AddOutgoingMessage(
327 persistent_ids.back(),
329 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
333 gcm_store = BuildGCMStore().Pass();
334 gcm_store->Load(base::Bind(
335 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
338 ASSERT_TRUE(load_result->incoming_messages.empty());
339 ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
340 for (int i = 0; i < kNumPersistentIds; ++i) {
341 std::string id = persistent_ids[i];
342 ASSERT_TRUE(load_result->outgoing_messages[id].get());
343 const mcs_proto::DataMessageStanza* message =
344 reinterpret_cast<mcs_proto::DataMessageStanza*>(
345 load_result->outgoing_messages[id].get());
346 ASSERT_EQ(message->from(), kAppName + id);
347 ASSERT_EQ(message->category(), kCategoryName + id);
350 gcm_store->RemoveOutgoingMessages(
352 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
355 gcm_store = BuildGCMStore().Pass();
356 load_result->outgoing_messages.clear();
357 gcm_store->Load(base::Bind(
358 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
361 ASSERT_TRUE(load_result->incoming_messages.empty());
362 ASSERT_TRUE(load_result->outgoing_messages.empty());
365 // Verify incoming and outgoing messages don't conflict.
366 TEST_F(GCMStoreImplTest, IncomingAndOutgoingMessages) {
367 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
368 scoped_ptr<GCMStore::LoadResult> load_result;
369 gcm_store->Load(base::Bind(
370 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
373 std::vector<std::string> persistent_ids;
374 const int kNumPersistentIds = 10;
375 for (int i = 0; i < kNumPersistentIds; ++i) {
376 persistent_ids.push_back(GetNextPersistentId());
377 gcm_store->AddIncomingMessage(
378 persistent_ids.back(),
379 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
382 mcs_proto::DataMessageStanza message;
383 message.set_from(kAppName + persistent_ids.back());
384 message.set_category(kCategoryName + persistent_ids.back());
385 gcm_store->AddOutgoingMessage(
386 persistent_ids.back(),
388 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
392 gcm_store = BuildGCMStore().Pass();
393 gcm_store->Load(base::Bind(
394 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
397 ASSERT_EQ(persistent_ids, load_result->incoming_messages);
398 ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
399 for (int i = 0; i < kNumPersistentIds; ++i) {
400 std::string id = persistent_ids[i];
401 ASSERT_TRUE(load_result->outgoing_messages[id].get());
402 const mcs_proto::DataMessageStanza* message =
403 reinterpret_cast<mcs_proto::DataMessageStanza*>(
404 load_result->outgoing_messages[id].get());
405 ASSERT_EQ(message->from(), kAppName + id);
406 ASSERT_EQ(message->category(), kCategoryName + id);
409 gcm_store->RemoveIncomingMessages(
411 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
413 gcm_store->RemoveOutgoingMessages(
415 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
418 gcm_store = BuildGCMStore().Pass();
419 load_result->incoming_messages.clear();
420 load_result->outgoing_messages.clear();
421 gcm_store->Load(base::Bind(
422 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
425 ASSERT_TRUE(load_result->incoming_messages.empty());
426 ASSERT_TRUE(load_result->outgoing_messages.empty());
429 // Test that per-app message limits are enforced, persisted across restarts,
430 // and updated as messages are removed.
431 TEST_F(GCMStoreImplTest, PerAppMessageLimits) {
432 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
433 scoped_ptr<GCMStore::LoadResult> load_result;
434 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
435 base::Unretained(this),
438 // Add the initial (below app limit) messages.
439 for (int i = 0; i < kNumMessagesPerApp; ++i) {
440 mcs_proto::DataMessageStanza message;
441 message.set_from(kAppName);
442 message.set_category(kCategoryName);
443 EXPECT_TRUE(gcm_store->AddOutgoingMessage(
444 base::IntToString(i),
446 base::Bind(&GCMStoreImplTest::UpdateCallback,
447 base::Unretained(this))));
451 // Attempting to add some more should fail.
452 for (int i = 0; i < kNumMessagesPerApp; ++i) {
453 mcs_proto::DataMessageStanza message;
454 message.set_from(kAppName);
455 message.set_category(kCategoryName);
456 EXPECT_FALSE(gcm_store->AddOutgoingMessage(
457 base::IntToString(i + kNumMessagesPerApp),
459 base::Bind(&GCMStoreImplTest::UpdateCallback,
460 base::Unretained(this))));
464 // Tear down and restore the database.
465 gcm_store = BuildGCMStore().Pass();
466 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
467 base::Unretained(this),
471 // Adding more messages should still fail.
472 for (int i = 0; i < kNumMessagesPerApp; ++i) {
473 mcs_proto::DataMessageStanza message;
474 message.set_from(kAppName);
475 message.set_category(kCategoryName);
476 EXPECT_FALSE(gcm_store->AddOutgoingMessage(
477 base::IntToString(i + kNumMessagesPerApp),
479 base::Bind(&GCMStoreImplTest::UpdateCallback,
480 base::Unretained(this))));
484 // Remove the existing messages.
485 for (int i = 0; i < kNumMessagesPerApp; ++i) {
486 gcm_store->RemoveOutgoingMessage(
487 base::IntToString(i),
488 base::Bind(&GCMStoreImplTest::UpdateCallback,
489 base::Unretained(this)));
493 // Successfully add new messages.
494 for (int i = 0; i < kNumMessagesPerApp; ++i) {
495 mcs_proto::DataMessageStanza message;
496 message.set_from(kAppName);
497 message.set_category(kCategoryName);
498 EXPECT_TRUE(gcm_store->AddOutgoingMessage(
499 base::IntToString(i + kNumMessagesPerApp),
501 base::Bind(&GCMStoreImplTest::UpdateCallback,
502 base::Unretained(this))));
507 // When the database is destroyed, all database updates should fail. At the
508 // same time, they per-app message counts should not go up, as failures should
509 // result in decrementing the counts.
510 TEST_F(GCMStoreImplTest, AddMessageAfterDestroy) {
511 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
512 scoped_ptr<GCMStore::LoadResult> load_result;
513 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
514 base::Unretained(this),
517 gcm_store->Destroy(base::Bind(&GCMStoreImplTest::UpdateCallback,
518 base::Unretained(this)));
521 expected_success_ = false;
522 for (int i = 0; i < kNumMessagesPerApp * 2; ++i) {
523 mcs_proto::DataMessageStanza message;
524 message.set_from(kAppName);
525 message.set_category(kCategoryName);
526 // Because all adds are failing, none should hit the per-app message limits.
527 EXPECT_TRUE(gcm_store->AddOutgoingMessage(
528 base::IntToString(i),
530 base::Bind(&GCMStoreImplTest::UpdateCallback,
531 base::Unretained(this))));
536 TEST_F(GCMStoreImplTest, ReloadAfterClose) {
537 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
538 scoped_ptr<GCMStore::LoadResult> load_result;
539 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
540 base::Unretained(this),
547 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
548 base::Unretained(this),