Upstream version 7.36.149.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 "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"
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 #if defined(OS_MACOSX)
80   base::CommandLine::ForCurrentProcess()->AppendSwitch(
81       os_crypt::switches::kUseMockKeychain);
82 #endif  // OS_MACOSX
83 }
84
85 scoped_ptr<GCMStore> GCMStoreImplTest::BuildGCMStore() {
86   return scoped_ptr<GCMStore>(new GCMStoreImpl(
87       temp_directory_.path(),
88       message_loop_.message_loop_proxy()));
89 }
90
91 std::string GCMStoreImplTest::GetNextPersistentId() {
92   return base::Uint64ToString(next_persistent_id_++);
93 }
94
95 void GCMStoreImplTest::PumpLoop() { message_loop_.RunUntilIdle(); }
96
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();
102   run_loop_->Quit();
103   run_loop_.reset(new base::RunLoop());
104 }
105
106 void GCMStoreImplTest::UpdateCallback(bool success) {
107   ASSERT_EQ(expected_success_, success);
108 }
109
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));
116   PumpLoop();
117
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);
124 }
125
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));
131   PumpLoop();
132
133   gcm_store->SetDeviceCredentials(
134       kDeviceId,
135       kDeviceToken,
136       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
137   PumpLoop();
138
139   gcm_store = BuildGCMStore().Pass();
140   gcm_store->Load(base::Bind(
141       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
142   PumpLoop();
143
144   ASSERT_EQ(kDeviceId, load_result->device_android_id);
145   ASSERT_EQ(kDeviceToken, load_result->device_security_token);
146 }
147
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));
153   PumpLoop();
154
155   base::Time last_checkin_time = base::Time::Now();
156
157   gcm_store->SetLastCheckinTime(
158       last_checkin_time,
159       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
160   PumpLoop();
161
162   gcm_store = BuildGCMStore().Pass();
163   gcm_store->Load(base::Bind(
164       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
165   PumpLoop();
166   ASSERT_EQ(last_checkin_time, load_result->last_checkin_time);
167 }
168
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));
174   PumpLoop();
175
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";
181
182   gcm_store->SetGServicesSettings(
183       settings,
184       digest,
185       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
186   PumpLoop();
187
188   gcm_store = BuildGCMStore().Pass();
189   gcm_store->Load(base::Bind(
190       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
191   PumpLoop();
192
193   ASSERT_EQ(settings, load_result->gservices_settings);
194   ASSERT_EQ(digest, load_result->gservices_digest);
195
196   // Remove some, and add some.
197   settings.clear();
198   settings["checkin_interval"] = "54321";
199   settings["registration_url"] = "http://registration.google.com";
200   digest = "digest2";
201
202   gcm_store->SetGServicesSettings(
203       settings,
204       digest,
205       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
206   PumpLoop();
207
208   gcm_store = BuildGCMStore().Pass();
209   gcm_store->Load(base::Bind(
210       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
211   PumpLoop();
212
213   ASSERT_EQ(settings, load_result->gservices_settings);
214   ASSERT_EQ(digest, load_result->gservices_digest);
215 }
216
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));
222   PumpLoop();
223
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(
229       "app1",
230       registration1,
231       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
232   PumpLoop();
233
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(
240       "app2",
241       registration2,
242       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
243   PumpLoop();
244
245   gcm_store = BuildGCMStore().Pass();
246   gcm_store->Load(base::Bind(
247       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
248   PumpLoop();
249
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]);
267 }
268
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));
276   PumpLoop();
277
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)));
284     PumpLoop();
285   }
286
287   gcm_store = BuildGCMStore().Pass();
288   gcm_store->Load(base::Bind(
289       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
290   PumpLoop();
291
292   ASSERT_EQ(persistent_ids, load_result->incoming_messages);
293   ASSERT_TRUE(load_result->outgoing_messages.empty());
294
295   gcm_store->RemoveIncomingMessages(
296       persistent_ids,
297       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
298   PumpLoop();
299
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));
304   PumpLoop();
305
306   ASSERT_TRUE(load_result->incoming_messages.empty());
307   ASSERT_TRUE(load_result->outgoing_messages.empty());
308 }
309
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));
317   PumpLoop();
318
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(),
328         MCSMessage(message),
329         base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
330     PumpLoop();
331   }
332
333   gcm_store = BuildGCMStore().Pass();
334   gcm_store->Load(base::Bind(
335       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
336   PumpLoop();
337
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);
348   }
349
350   gcm_store->RemoveOutgoingMessages(
351       persistent_ids,
352       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
353   PumpLoop();
354
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));
359   PumpLoop();
360
361   ASSERT_TRUE(load_result->incoming_messages.empty());
362   ASSERT_TRUE(load_result->outgoing_messages.empty());
363 }
364
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));
371   PumpLoop();
372
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)));
380     PumpLoop();
381
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(),
387         MCSMessage(message),
388         base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
389     PumpLoop();
390   }
391
392   gcm_store = BuildGCMStore().Pass();
393   gcm_store->Load(base::Bind(
394       &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
395   PumpLoop();
396
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);
407   }
408
409   gcm_store->RemoveIncomingMessages(
410       persistent_ids,
411       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
412   PumpLoop();
413   gcm_store->RemoveOutgoingMessages(
414       persistent_ids,
415       base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
416   PumpLoop();
417
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));
423   PumpLoop();
424
425   ASSERT_TRUE(load_result->incoming_messages.empty());
426   ASSERT_TRUE(load_result->outgoing_messages.empty());
427 }
428
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),
436                              &load_result));
437
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),
445                     MCSMessage(message),
446                     base::Bind(&GCMStoreImplTest::UpdateCallback,
447                                base::Unretained(this))));
448     PumpLoop();
449   }
450
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),
458                      MCSMessage(message),
459                      base::Bind(&GCMStoreImplTest::UpdateCallback,
460                                 base::Unretained(this))));
461     PumpLoop();
462   }
463
464   // Tear down and restore the database.
465   gcm_store = BuildGCMStore().Pass();
466   gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
467                              base::Unretained(this),
468                              &load_result));
469   PumpLoop();
470
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),
478                      MCSMessage(message),
479                      base::Bind(&GCMStoreImplTest::UpdateCallback,
480                                 base::Unretained(this))));
481     PumpLoop();
482   }
483
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)));
490     PumpLoop();
491   }
492
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),
500                     MCSMessage(message),
501                     base::Bind(&GCMStoreImplTest::UpdateCallback,
502                                base::Unretained(this))));
503     PumpLoop();
504   }
505 }
506
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),
515                              &load_result));
516   PumpLoop();
517   gcm_store->Destroy(base::Bind(&GCMStoreImplTest::UpdateCallback,
518                                base::Unretained(this)));
519   PumpLoop();
520
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),
529                     MCSMessage(message),
530                     base::Bind(&GCMStoreImplTest::UpdateCallback,
531                                base::Unretained(this))));
532     PumpLoop();
533   }
534 }
535
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),
541                              &load_result));
542   PumpLoop();
543
544   gcm_store->Close();
545   PumpLoop();
546
547   gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
548                              base::Unretained(this),
549                              &load_result));
550   PumpLoop();
551 }
552
553 }  // namespace
554
555 }  // namespace gcm