Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / google_apis / gcm / engine / gcm_store_impl_unittest.cc
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.
4
5 #include "google_apis/gcm/engine/gcm_store_impl.h"
6
7 #include <string>
8 #include <vector>
9
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"
23
24 namespace gcm {
25
26 namespace {
27
28 // Number of persistent ids to use in tests.
29 const int kNumPersistentIds = 10;
30
31 // Number of per-app messages in tests.
32 const int kNumMessagesPerApp = 20;
33
34 // App name for testing.
35 const char kAppName[] = "my_app";
36
37 // Category name for testing.
38 const char kCategoryName[] = "my_category";
39
40 const uint64 kDeviceId = 22;
41 const uint64 kDeviceToken = 55;
42
43 class GCMStoreImplTest : public testing::Test {
44  public:
45   GCMStoreImplTest();
46   virtual ~GCMStoreImplTest();
47
48   virtual void SetUp() OVERRIDE;
49
50   scoped_ptr<GCMStore> BuildGCMStore();
51
52   std::string GetNextPersistentId();
53
54   void PumpLoop();
55
56   void LoadCallback(scoped_ptr<GCMStore::LoadResult>* result_dst,
57                     scoped_ptr<GCMStore::LoadResult> result);
58   void UpdateCallback(bool success);
59
60  protected:
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_;
66 };
67
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());
73 }
74
75 GCMStoreImplTest::~GCMStoreImplTest() {}
76
77 void GCMStoreImplTest::SetUp() {
78   testing::Test::SetUp();
79 }
80
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)));
86 }
87
88 std::string GCMStoreImplTest::GetNextPersistentId() {
89   return base::Uint64ToString(next_persistent_id_++);
90 }
91
92 void GCMStoreImplTest::PumpLoop() { message_loop_.RunUntilIdle(); }
93
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();
99   run_loop_->Quit();
100   run_loop_.reset(new base::RunLoop());
101 }
102
103 void GCMStoreImplTest::UpdateCallback(bool success) {
104   ASSERT_EQ(expected_success_, success);
105 }
106
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));
113   PumpLoop();
114
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);
121 }
122
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));
128   PumpLoop();
129
130   gcm_store->SetDeviceCredentials(
131       kDeviceId,
132       kDeviceToken,
133       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
134   PumpLoop();
135
136   gcm_store = BuildGCMStore().Pass();
137   gcm_store->Load(base::Bind(
138       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
139   PumpLoop();
140
141   ASSERT_EQ(kDeviceId, load_result->device_android_id);
142   ASSERT_EQ(kDeviceToken, load_result->device_security_token);
143 }
144
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));
150   PumpLoop();
151
152   base::Time last_checkin_time = base::Time::Now();
153
154   gcm_store->SetLastCheckinTime(
155       last_checkin_time,
156       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
157   PumpLoop();
158
159   gcm_store = BuildGCMStore().Pass();
160   gcm_store->Load(base::Bind(
161       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
162   PumpLoop();
163   ASSERT_EQ(last_checkin_time, load_result->last_checkin_time);
164 }
165
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));
171   PumpLoop();
172
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";
178
179   gcm_store->SetGServicesSettings(
180       settings,
181       digest,
182       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
183   PumpLoop();
184
185   gcm_store = BuildGCMStore().Pass();
186   gcm_store->Load(base::Bind(
187       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
188   PumpLoop();
189
190   ASSERT_EQ(settings, load_result->gservices_settings);
191   ASSERT_EQ(digest, load_result->gservices_digest);
192
193   // Remove some, and add some.
194   settings.clear();
195   settings["checkin_interval"] = "54321";
196   settings["registration_url"] = "http://registration.google.com";
197   digest = "digest2";
198
199   gcm_store->SetGServicesSettings(
200       settings,
201       digest,
202       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
203   PumpLoop();
204
205   gcm_store = BuildGCMStore().Pass();
206   gcm_store->Load(base::Bind(
207       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
208   PumpLoop();
209
210   ASSERT_EQ(settings, load_result->gservices_settings);
211   ASSERT_EQ(digest, load_result->gservices_digest);
212 }
213
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));
219   PumpLoop();
220
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(
226       "app1",
227       registration1,
228       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
229   PumpLoop();
230
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(
237       "app2",
238       registration2,
239       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
240   PumpLoop();
241
242   gcm_store = BuildGCMStore().Pass();
243   gcm_store->Load(base::Bind(
244       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
245   PumpLoop();
246
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]);
264
265   gcm_store->RemoveRegistration(
266       "app2",
267       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
268   PumpLoop();
269
270   gcm_store = BuildGCMStore().Pass();
271   gcm_store->Load(base::Bind(
272       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
273   PumpLoop();
274
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]);
283 }
284
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));
292   PumpLoop();
293
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)));
300     PumpLoop();
301   }
302
303   gcm_store = BuildGCMStore().Pass();
304   gcm_store->Load(base::Bind(
305       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
306   PumpLoop();
307
308   ASSERT_EQ(persistent_ids, load_result->incoming_messages);
309   ASSERT_TRUE(load_result->outgoing_messages.empty());
310
311   gcm_store->RemoveIncomingMessages(
312       persistent_ids,
313       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
314   PumpLoop();
315
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));
320   PumpLoop();
321
322   ASSERT_TRUE(load_result->incoming_messages.empty());
323   ASSERT_TRUE(load_result->outgoing_messages.empty());
324 }
325
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));
333   PumpLoop();
334
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(),
344         MCSMessage(message),
345         base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
346     PumpLoop();
347   }
348
349   gcm_store = BuildGCMStore().Pass();
350   gcm_store->Load(base::Bind(
351       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
352   PumpLoop();
353
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);
364   }
365
366   gcm_store->RemoveOutgoingMessages(
367       persistent_ids,
368       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
369   PumpLoop();
370
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));
375   PumpLoop();
376
377   ASSERT_TRUE(load_result->incoming_messages.empty());
378   ASSERT_TRUE(load_result->outgoing_messages.empty());
379 }
380
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));
387   PumpLoop();
388
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)));
396     PumpLoop();
397
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(),
403         MCSMessage(message),
404         base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
405     PumpLoop();
406   }
407
408   gcm_store = BuildGCMStore().Pass();
409   gcm_store->Load(base::Bind(
410       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
411   PumpLoop();
412
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);
423   }
424
425   gcm_store->RemoveIncomingMessages(
426       persistent_ids,
427       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
428   PumpLoop();
429   gcm_store->RemoveOutgoingMessages(
430       persistent_ids,
431       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
432   PumpLoop();
433
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));
439   PumpLoop();
440
441   ASSERT_TRUE(load_result->incoming_messages.empty());
442   ASSERT_TRUE(load_result->outgoing_messages.empty());
443 }
444
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),
452                              &load_result));
453
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),
461                     MCSMessage(message),
462                     base::Bind(&GCMStoreImplTest::UpdateCallback,
463                                base::Unretained(this))));
464     PumpLoop();
465   }
466
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),
474                      MCSMessage(message),
475                      base::Bind(&GCMStoreImplTest::UpdateCallback,
476                                 base::Unretained(this))));
477     PumpLoop();
478   }
479
480   // Tear down and restore the database.
481   gcm_store = BuildGCMStore().Pass();
482   gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
483                              base::Unretained(this),
484                              &load_result));
485   PumpLoop();
486
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),
494                      MCSMessage(message),
495                      base::Bind(&GCMStoreImplTest::UpdateCallback,
496                                 base::Unretained(this))));
497     PumpLoop();
498   }
499
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)));
506     PumpLoop();
507   }
508
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),
516                     MCSMessage(message),
517                     base::Bind(&GCMStoreImplTest::UpdateCallback,
518                                base::Unretained(this))));
519     PumpLoop();
520   }
521 }
522
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),
531                              &load_result));
532   PumpLoop();
533   gcm_store->Destroy(base::Bind(&GCMStoreImplTest::UpdateCallback,
534                                base::Unretained(this)));
535   PumpLoop();
536
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),
545                     MCSMessage(message),
546                     base::Bind(&GCMStoreImplTest::UpdateCallback,
547                                base::Unretained(this))));
548     PumpLoop();
549   }
550 }
551
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),
557                              &load_result));
558   PumpLoop();
559
560   gcm_store->Close();
561   PumpLoop();
562
563   gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
564                              base::Unretained(this),
565                              &load_result));
566   PumpLoop();
567 }
568
569 }  // namespace
570
571 }  // namespace gcm