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 "google_apis/gcm/base/fake_encryptor.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();
81 scoped_ptr
<GCMStore
> GCMStoreImplTest::BuildGCMStore() {
82 return scoped_ptr
<GCMStore
>(new GCMStoreImpl(
83 temp_directory_
.path(),
84 message_loop_
.message_loop_proxy(),
85 make_scoped_ptr
<Encryptor
>(new FakeEncryptor
)));
88 std::string
GCMStoreImplTest::GetNextPersistentId() {
89 return base::Uint64ToString(next_persistent_id_
++);
92 void GCMStoreImplTest::PumpLoop() { message_loop_
.RunUntilIdle(); }
94 void GCMStoreImplTest::LoadCallback(
95 scoped_ptr
<GCMStore::LoadResult
>* result_dst
,
96 scoped_ptr
<GCMStore::LoadResult
> result
) {
97 ASSERT_TRUE(result
->success
);
98 *result_dst
= result
.Pass();
100 run_loop_
.reset(new base::RunLoop());
103 void GCMStoreImplTest::UpdateCallback(bool success
) {
104 ASSERT_EQ(expected_success_
, success
);
107 // Verify creating a new database and loading it.
108 TEST_F(GCMStoreImplTest
, LoadNew
) {
109 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
110 scoped_ptr
<GCMStore::LoadResult
> load_result
;
111 gcm_store
->Load(base::Bind(
112 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
115 EXPECT_EQ(0U, load_result
->device_android_id
);
116 EXPECT_EQ(0U, load_result
->device_security_token
);
117 EXPECT_TRUE(load_result
->incoming_messages
.empty());
118 EXPECT_TRUE(load_result
->outgoing_messages
.empty());
119 EXPECT_TRUE(load_result
->gservices_settings
.empty());
120 EXPECT_EQ(base::Time::FromInternalValue(0LL), load_result
->last_checkin_time
);
123 TEST_F(GCMStoreImplTest
, DeviceCredentials
) {
124 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
125 scoped_ptr
<GCMStore::LoadResult
> load_result
;
126 gcm_store
->Load(base::Bind(
127 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
130 gcm_store
->SetDeviceCredentials(
133 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
136 gcm_store
= BuildGCMStore().Pass();
137 gcm_store
->Load(base::Bind(
138 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
141 ASSERT_EQ(kDeviceId
, load_result
->device_android_id
);
142 ASSERT_EQ(kDeviceToken
, load_result
->device_security_token
);
145 TEST_F(GCMStoreImplTest
, LastCheckinTime
) {
146 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
147 scoped_ptr
<GCMStore::LoadResult
> load_result
;
148 gcm_store
->Load(base::Bind(
149 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
152 base::Time last_checkin_time
= base::Time::Now();
154 gcm_store
->SetLastCheckinTime(
156 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
159 gcm_store
= BuildGCMStore().Pass();
160 gcm_store
->Load(base::Bind(
161 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
163 ASSERT_EQ(last_checkin_time
, load_result
->last_checkin_time
);
166 TEST_F(GCMStoreImplTest
, GServicesSettings_ProtocolV2
) {
167 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
168 scoped_ptr
<GCMStore::LoadResult
> load_result
;
169 gcm_store
->Load(base::Bind(
170 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
173 std::map
<std::string
, std::string
> settings
;
174 settings
["checkin_interval"] = "12345";
175 settings
["mcs_port"] = "438";
176 settings
["checkin_url"] = "http://checkin.google.com";
177 std::string digest
= "digest1";
179 gcm_store
->SetGServicesSettings(
182 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
185 gcm_store
= BuildGCMStore().Pass();
186 gcm_store
->Load(base::Bind(
187 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
190 ASSERT_EQ(settings
, load_result
->gservices_settings
);
191 ASSERT_EQ(digest
, load_result
->gservices_digest
);
193 // Remove some, and add some.
195 settings
["checkin_interval"] = "54321";
196 settings
["registration_url"] = "http://registration.google.com";
199 gcm_store
->SetGServicesSettings(
202 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
205 gcm_store
= BuildGCMStore().Pass();
206 gcm_store
->Load(base::Bind(
207 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
210 ASSERT_EQ(settings
, load_result
->gservices_settings
);
211 ASSERT_EQ(digest
, load_result
->gservices_digest
);
214 TEST_F(GCMStoreImplTest
, Registrations
) {
215 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
216 scoped_ptr
<GCMStore::LoadResult
> load_result
;
217 gcm_store
->Load(base::Bind(
218 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
221 // Add one registration with one sender.
222 linked_ptr
<RegistrationInfo
> registration1(new RegistrationInfo
);
223 registration1
->sender_ids
.push_back("sender1");
224 registration1
->registration_id
= "registration1";
225 gcm_store
->AddRegistration(
228 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
231 // Add one registration with multiple senders.
232 linked_ptr
<RegistrationInfo
> registration2(new RegistrationInfo
);
233 registration2
->sender_ids
.push_back("sender2_1");
234 registration2
->sender_ids
.push_back("sender2_2");
235 registration2
->registration_id
= "registration2";
236 gcm_store
->AddRegistration(
239 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
242 gcm_store
= BuildGCMStore().Pass();
243 gcm_store
->Load(base::Bind(
244 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
247 ASSERT_EQ(2u, load_result
->registrations
.size());
248 ASSERT_TRUE(load_result
->registrations
.find("app1") !=
249 load_result
->registrations
.end());
250 EXPECT_EQ(registration1
->registration_id
,
251 load_result
->registrations
["app1"]->registration_id
);
252 ASSERT_EQ(1u, load_result
->registrations
["app1"]->sender_ids
.size());
253 EXPECT_EQ(registration1
->sender_ids
[0],
254 load_result
->registrations
["app1"]->sender_ids
[0]);
255 ASSERT_TRUE(load_result
->registrations
.find("app2") !=
256 load_result
->registrations
.end());
257 EXPECT_EQ(registration2
->registration_id
,
258 load_result
->registrations
["app2"]->registration_id
);
259 ASSERT_EQ(2u, load_result
->registrations
["app2"]->sender_ids
.size());
260 EXPECT_EQ(registration2
->sender_ids
[0],
261 load_result
->registrations
["app2"]->sender_ids
[0]);
262 EXPECT_EQ(registration2
->sender_ids
[1],
263 load_result
->registrations
["app2"]->sender_ids
[1]);
266 // Verify saving some incoming messages, reopening the directory, and then
267 // removing those incoming messages.
268 TEST_F(GCMStoreImplTest
, IncomingMessages
) {
269 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
270 scoped_ptr
<GCMStore::LoadResult
> load_result
;
271 gcm_store
->Load(base::Bind(
272 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
275 std::vector
<std::string
> persistent_ids
;
276 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
277 persistent_ids
.push_back(GetNextPersistentId());
278 gcm_store
->AddIncomingMessage(
279 persistent_ids
.back(),
280 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
284 gcm_store
= BuildGCMStore().Pass();
285 gcm_store
->Load(base::Bind(
286 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
289 ASSERT_EQ(persistent_ids
, load_result
->incoming_messages
);
290 ASSERT_TRUE(load_result
->outgoing_messages
.empty());
292 gcm_store
->RemoveIncomingMessages(
294 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
297 gcm_store
= BuildGCMStore().Pass();
298 load_result
->incoming_messages
.clear();
299 gcm_store
->Load(base::Bind(
300 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
303 ASSERT_TRUE(load_result
->incoming_messages
.empty());
304 ASSERT_TRUE(load_result
->outgoing_messages
.empty());
307 // Verify saving some outgoing messages, reopening the directory, and then
308 // removing those outgoing messages.
309 TEST_F(GCMStoreImplTest
, OutgoingMessages
) {
310 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
311 scoped_ptr
<GCMStore::LoadResult
> load_result
;
312 gcm_store
->Load(base::Bind(
313 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
316 std::vector
<std::string
> persistent_ids
;
317 const int kNumPersistentIds
= 10;
318 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
319 persistent_ids
.push_back(GetNextPersistentId());
320 mcs_proto::DataMessageStanza message
;
321 message
.set_from(kAppName
+ persistent_ids
.back());
322 message
.set_category(kCategoryName
+ persistent_ids
.back());
323 gcm_store
->AddOutgoingMessage(
324 persistent_ids
.back(),
326 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
330 gcm_store
= BuildGCMStore().Pass();
331 gcm_store
->Load(base::Bind(
332 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
335 ASSERT_TRUE(load_result
->incoming_messages
.empty());
336 ASSERT_EQ(load_result
->outgoing_messages
.size(), persistent_ids
.size());
337 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
338 std::string id
= persistent_ids
[i
];
339 ASSERT_TRUE(load_result
->outgoing_messages
[id
].get());
340 const mcs_proto::DataMessageStanza
* message
=
341 reinterpret_cast<mcs_proto::DataMessageStanza
*>(
342 load_result
->outgoing_messages
[id
].get());
343 ASSERT_EQ(message
->from(), kAppName
+ id
);
344 ASSERT_EQ(message
->category(), kCategoryName
+ id
);
347 gcm_store
->RemoveOutgoingMessages(
349 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
352 gcm_store
= BuildGCMStore().Pass();
353 load_result
->outgoing_messages
.clear();
354 gcm_store
->Load(base::Bind(
355 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
358 ASSERT_TRUE(load_result
->incoming_messages
.empty());
359 ASSERT_TRUE(load_result
->outgoing_messages
.empty());
362 // Verify incoming and outgoing messages don't conflict.
363 TEST_F(GCMStoreImplTest
, IncomingAndOutgoingMessages
) {
364 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
365 scoped_ptr
<GCMStore::LoadResult
> load_result
;
366 gcm_store
->Load(base::Bind(
367 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
370 std::vector
<std::string
> persistent_ids
;
371 const int kNumPersistentIds
= 10;
372 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
373 persistent_ids
.push_back(GetNextPersistentId());
374 gcm_store
->AddIncomingMessage(
375 persistent_ids
.back(),
376 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
379 mcs_proto::DataMessageStanza message
;
380 message
.set_from(kAppName
+ persistent_ids
.back());
381 message
.set_category(kCategoryName
+ persistent_ids
.back());
382 gcm_store
->AddOutgoingMessage(
383 persistent_ids
.back(),
385 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
389 gcm_store
= BuildGCMStore().Pass();
390 gcm_store
->Load(base::Bind(
391 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
394 ASSERT_EQ(persistent_ids
, load_result
->incoming_messages
);
395 ASSERT_EQ(load_result
->outgoing_messages
.size(), persistent_ids
.size());
396 for (int i
= 0; i
< kNumPersistentIds
; ++i
) {
397 std::string id
= persistent_ids
[i
];
398 ASSERT_TRUE(load_result
->outgoing_messages
[id
].get());
399 const mcs_proto::DataMessageStanza
* message
=
400 reinterpret_cast<mcs_proto::DataMessageStanza
*>(
401 load_result
->outgoing_messages
[id
].get());
402 ASSERT_EQ(message
->from(), kAppName
+ id
);
403 ASSERT_EQ(message
->category(), kCategoryName
+ id
);
406 gcm_store
->RemoveIncomingMessages(
408 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
410 gcm_store
->RemoveOutgoingMessages(
412 base::Bind(&GCMStoreImplTest::UpdateCallback
, base::Unretained(this)));
415 gcm_store
= BuildGCMStore().Pass();
416 load_result
->incoming_messages
.clear();
417 load_result
->outgoing_messages
.clear();
418 gcm_store
->Load(base::Bind(
419 &GCMStoreImplTest::LoadCallback
, base::Unretained(this), &load_result
));
422 ASSERT_TRUE(load_result
->incoming_messages
.empty());
423 ASSERT_TRUE(load_result
->outgoing_messages
.empty());
426 // Test that per-app message limits are enforced, persisted across restarts,
427 // and updated as messages are removed.
428 TEST_F(GCMStoreImplTest
, PerAppMessageLimits
) {
429 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
430 scoped_ptr
<GCMStore::LoadResult
> load_result
;
431 gcm_store
->Load(base::Bind(&GCMStoreImplTest::LoadCallback
,
432 base::Unretained(this),
435 // Add the initial (below app limit) messages.
436 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
437 mcs_proto::DataMessageStanza message
;
438 message
.set_from(kAppName
);
439 message
.set_category(kCategoryName
);
440 EXPECT_TRUE(gcm_store
->AddOutgoingMessage(
441 base::IntToString(i
),
443 base::Bind(&GCMStoreImplTest::UpdateCallback
,
444 base::Unretained(this))));
448 // Attempting to add some more should fail.
449 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
450 mcs_proto::DataMessageStanza message
;
451 message
.set_from(kAppName
);
452 message
.set_category(kCategoryName
);
453 EXPECT_FALSE(gcm_store
->AddOutgoingMessage(
454 base::IntToString(i
+ kNumMessagesPerApp
),
456 base::Bind(&GCMStoreImplTest::UpdateCallback
,
457 base::Unretained(this))));
461 // Tear down and restore the database.
462 gcm_store
= BuildGCMStore().Pass();
463 gcm_store
->Load(base::Bind(&GCMStoreImplTest::LoadCallback
,
464 base::Unretained(this),
468 // Adding more messages should still fail.
469 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
470 mcs_proto::DataMessageStanza message
;
471 message
.set_from(kAppName
);
472 message
.set_category(kCategoryName
);
473 EXPECT_FALSE(gcm_store
->AddOutgoingMessage(
474 base::IntToString(i
+ kNumMessagesPerApp
),
476 base::Bind(&GCMStoreImplTest::UpdateCallback
,
477 base::Unretained(this))));
481 // Remove the existing messages.
482 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
483 gcm_store
->RemoveOutgoingMessage(
484 base::IntToString(i
),
485 base::Bind(&GCMStoreImplTest::UpdateCallback
,
486 base::Unretained(this)));
490 // Successfully add new messages.
491 for (int i
= 0; i
< kNumMessagesPerApp
; ++i
) {
492 mcs_proto::DataMessageStanza message
;
493 message
.set_from(kAppName
);
494 message
.set_category(kCategoryName
);
495 EXPECT_TRUE(gcm_store
->AddOutgoingMessage(
496 base::IntToString(i
+ kNumMessagesPerApp
),
498 base::Bind(&GCMStoreImplTest::UpdateCallback
,
499 base::Unretained(this))));
504 // When the database is destroyed, all database updates should fail. At the
505 // same time, they per-app message counts should not go up, as failures should
506 // result in decrementing the counts.
507 TEST_F(GCMStoreImplTest
, AddMessageAfterDestroy
) {
508 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
509 scoped_ptr
<GCMStore::LoadResult
> load_result
;
510 gcm_store
->Load(base::Bind(&GCMStoreImplTest::LoadCallback
,
511 base::Unretained(this),
514 gcm_store
->Destroy(base::Bind(&GCMStoreImplTest::UpdateCallback
,
515 base::Unretained(this)));
518 expected_success_
= false;
519 for (int i
= 0; i
< kNumMessagesPerApp
* 2; ++i
) {
520 mcs_proto::DataMessageStanza message
;
521 message
.set_from(kAppName
);
522 message
.set_category(kCategoryName
);
523 // Because all adds are failing, none should hit the per-app message limits.
524 EXPECT_TRUE(gcm_store
->AddOutgoingMessage(
525 base::IntToString(i
),
527 base::Bind(&GCMStoreImplTest::UpdateCallback
,
528 base::Unretained(this))));
533 TEST_F(GCMStoreImplTest
, ReloadAfterClose
) {
534 scoped_ptr
<GCMStore
> gcm_store(BuildGCMStore());
535 scoped_ptr
<GCMStore::LoadResult
> load_result
;
536 gcm_store
->Load(base::Bind(&GCMStoreImplTest::LoadCallback
,
537 base::Unretained(this),
544 gcm_store
->Load(base::Bind(&GCMStoreImplTest::LoadCallback
,
545 base::Unretained(this),