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]);
265 gcm_store->RemoveRegistration(
267 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
270 gcm_store = BuildGCMStore().Pass();
271 gcm_store->Load(base::Bind(
272 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
275 ASSERT_EQ(1u, load_result->registrations.size());
276 ASSERT_TRUE(load_result->registrations.find("app1") !=
277 load_result->registrations.end());
278 EXPECT_EQ(registration1->registration_id,
279 load_result->registrations["app1"]->registration_id);
280 ASSERT_EQ(1u, load_result->registrations["app1"]->sender_ids.size());
281 EXPECT_EQ(registration1->sender_ids[0],
282 load_result->registrations["app1"]->sender_ids[0]);
285 // Verify saving some incoming messages, reopening the directory, and then
286 // removing those incoming messages.
287 TEST_F(GCMStoreImplTest, IncomingMessages) {
288 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
289 scoped_ptr<GCMStore::LoadResult> load_result;
290 gcm_store->Load(base::Bind(
291 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
294 std::vector<std::string> persistent_ids;
295 for (int i = 0; i < kNumPersistentIds; ++i) {
296 persistent_ids.push_back(GetNextPersistentId());
297 gcm_store->AddIncomingMessage(
298 persistent_ids.back(),
299 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
303 gcm_store = BuildGCMStore().Pass();
304 gcm_store->Load(base::Bind(
305 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
308 ASSERT_EQ(persistent_ids, load_result->incoming_messages);
309 ASSERT_TRUE(load_result->outgoing_messages.empty());
311 gcm_store->RemoveIncomingMessages(
313 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
316 gcm_store = BuildGCMStore().Pass();
317 load_result->incoming_messages.clear();
318 gcm_store->Load(base::Bind(
319 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
322 ASSERT_TRUE(load_result->incoming_messages.empty());
323 ASSERT_TRUE(load_result->outgoing_messages.empty());
326 // Verify saving some outgoing messages, reopening the directory, and then
327 // removing those outgoing messages.
328 TEST_F(GCMStoreImplTest, OutgoingMessages) {
329 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
330 scoped_ptr<GCMStore::LoadResult> load_result;
331 gcm_store->Load(base::Bind(
332 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
335 std::vector<std::string> persistent_ids;
336 const int kNumPersistentIds = 10;
337 for (int i = 0; i < kNumPersistentIds; ++i) {
338 persistent_ids.push_back(GetNextPersistentId());
339 mcs_proto::DataMessageStanza message;
340 message.set_from(kAppName + persistent_ids.back());
341 message.set_category(kCategoryName + persistent_ids.back());
342 gcm_store->AddOutgoingMessage(
343 persistent_ids.back(),
345 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
349 gcm_store = BuildGCMStore().Pass();
350 gcm_store->Load(base::Bind(
351 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
354 ASSERT_TRUE(load_result->incoming_messages.empty());
355 ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
356 for (int i = 0; i < kNumPersistentIds; ++i) {
357 std::string id = persistent_ids[i];
358 ASSERT_TRUE(load_result->outgoing_messages[id].get());
359 const mcs_proto::DataMessageStanza* message =
360 reinterpret_cast<mcs_proto::DataMessageStanza*>(
361 load_result->outgoing_messages[id].get());
362 ASSERT_EQ(message->from(), kAppName + id);
363 ASSERT_EQ(message->category(), kCategoryName + id);
366 gcm_store->RemoveOutgoingMessages(
368 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
371 gcm_store = BuildGCMStore().Pass();
372 load_result->outgoing_messages.clear();
373 gcm_store->Load(base::Bind(
374 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
377 ASSERT_TRUE(load_result->incoming_messages.empty());
378 ASSERT_TRUE(load_result->outgoing_messages.empty());
381 // Verify incoming and outgoing messages don't conflict.
382 TEST_F(GCMStoreImplTest, IncomingAndOutgoingMessages) {
383 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
384 scoped_ptr<GCMStore::LoadResult> load_result;
385 gcm_store->Load(base::Bind(
386 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
389 std::vector<std::string> persistent_ids;
390 const int kNumPersistentIds = 10;
391 for (int i = 0; i < kNumPersistentIds; ++i) {
392 persistent_ids.push_back(GetNextPersistentId());
393 gcm_store->AddIncomingMessage(
394 persistent_ids.back(),
395 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
398 mcs_proto::DataMessageStanza message;
399 message.set_from(kAppName + persistent_ids.back());
400 message.set_category(kCategoryName + persistent_ids.back());
401 gcm_store->AddOutgoingMessage(
402 persistent_ids.back(),
404 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
408 gcm_store = BuildGCMStore().Pass();
409 gcm_store->Load(base::Bind(
410 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
413 ASSERT_EQ(persistent_ids, load_result->incoming_messages);
414 ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
415 for (int i = 0; i < kNumPersistentIds; ++i) {
416 std::string id = persistent_ids[i];
417 ASSERT_TRUE(load_result->outgoing_messages[id].get());
418 const mcs_proto::DataMessageStanza* message =
419 reinterpret_cast<mcs_proto::DataMessageStanza*>(
420 load_result->outgoing_messages[id].get());
421 ASSERT_EQ(message->from(), kAppName + id);
422 ASSERT_EQ(message->category(), kCategoryName + id);
425 gcm_store->RemoveIncomingMessages(
427 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
429 gcm_store->RemoveOutgoingMessages(
431 base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
434 gcm_store = BuildGCMStore().Pass();
435 load_result->incoming_messages.clear();
436 load_result->outgoing_messages.clear();
437 gcm_store->Load(base::Bind(
438 &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
441 ASSERT_TRUE(load_result->incoming_messages.empty());
442 ASSERT_TRUE(load_result->outgoing_messages.empty());
445 // Test that per-app message limits are enforced, persisted across restarts,
446 // and updated as messages are removed.
447 TEST_F(GCMStoreImplTest, PerAppMessageLimits) {
448 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
449 scoped_ptr<GCMStore::LoadResult> load_result;
450 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
451 base::Unretained(this),
454 // Add the initial (below app limit) messages.
455 for (int i = 0; i < kNumMessagesPerApp; ++i) {
456 mcs_proto::DataMessageStanza message;
457 message.set_from(kAppName);
458 message.set_category(kCategoryName);
459 EXPECT_TRUE(gcm_store->AddOutgoingMessage(
460 base::IntToString(i),
462 base::Bind(&GCMStoreImplTest::UpdateCallback,
463 base::Unretained(this))));
467 // Attempting to add some more should fail.
468 for (int i = 0; i < kNumMessagesPerApp; ++i) {
469 mcs_proto::DataMessageStanza message;
470 message.set_from(kAppName);
471 message.set_category(kCategoryName);
472 EXPECT_FALSE(gcm_store->AddOutgoingMessage(
473 base::IntToString(i + kNumMessagesPerApp),
475 base::Bind(&GCMStoreImplTest::UpdateCallback,
476 base::Unretained(this))));
480 // Tear down and restore the database.
481 gcm_store = BuildGCMStore().Pass();
482 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
483 base::Unretained(this),
487 // Adding more messages should still fail.
488 for (int i = 0; i < kNumMessagesPerApp; ++i) {
489 mcs_proto::DataMessageStanza message;
490 message.set_from(kAppName);
491 message.set_category(kCategoryName);
492 EXPECT_FALSE(gcm_store->AddOutgoingMessage(
493 base::IntToString(i + kNumMessagesPerApp),
495 base::Bind(&GCMStoreImplTest::UpdateCallback,
496 base::Unretained(this))));
500 // Remove the existing messages.
501 for (int i = 0; i < kNumMessagesPerApp; ++i) {
502 gcm_store->RemoveOutgoingMessage(
503 base::IntToString(i),
504 base::Bind(&GCMStoreImplTest::UpdateCallback,
505 base::Unretained(this)));
509 // Successfully add new messages.
510 for (int i = 0; i < kNumMessagesPerApp; ++i) {
511 mcs_proto::DataMessageStanza message;
512 message.set_from(kAppName);
513 message.set_category(kCategoryName);
514 EXPECT_TRUE(gcm_store->AddOutgoingMessage(
515 base::IntToString(i + kNumMessagesPerApp),
517 base::Bind(&GCMStoreImplTest::UpdateCallback,
518 base::Unretained(this))));
523 // When the database is destroyed, all database updates should fail. At the
524 // same time, they per-app message counts should not go up, as failures should
525 // result in decrementing the counts.
526 TEST_F(GCMStoreImplTest, AddMessageAfterDestroy) {
527 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
528 scoped_ptr<GCMStore::LoadResult> load_result;
529 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
530 base::Unretained(this),
533 gcm_store->Destroy(base::Bind(&GCMStoreImplTest::UpdateCallback,
534 base::Unretained(this)));
537 expected_success_ = false;
538 for (int i = 0; i < kNumMessagesPerApp * 2; ++i) {
539 mcs_proto::DataMessageStanza message;
540 message.set_from(kAppName);
541 message.set_category(kCategoryName);
542 // Because all adds are failing, none should hit the per-app message limits.
543 EXPECT_TRUE(gcm_store->AddOutgoingMessage(
544 base::IntToString(i),
546 base::Bind(&GCMStoreImplTest::UpdateCallback,
547 base::Unretained(this))));
552 TEST_F(GCMStoreImplTest, ReloadAfterClose) {
553 scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
554 scoped_ptr<GCMStore::LoadResult> load_result;
555 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
556 base::Unretained(this),
563 gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
564 base::Unretained(this),