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
24namespace gcm {
25
26namespace {
27
28// Number of persistent ids to use in tests.
29const int kNumPersistentIds = 10;
30
31// Number of per-app messages in tests.
32const int kNumMessagesPerApp = 20;
33
34// App name for testing.
35const char kAppName[] = "my_app";
36
37// Category name for testing.
38const char kCategoryName[] = "my_category";
39
40const uint64 kDeviceId = 22;
41const uint64 kDeviceToken = 55;
42
43class GCMStoreImplTest : public testing::Test {
44 public:
45  GCMStoreImplTest();
46  virtual ~GCMStoreImplTest();
47
48  scoped_ptr<GCMStore> BuildGCMStore();
49
50  std::string GetNextPersistentId();
51
52  void PumpLoop();
53
54  void LoadCallback(scoped_ptr<GCMStore::LoadResult>* result_dst,
55                    scoped_ptr<GCMStore::LoadResult> result);
56  void UpdateCallback(bool success);
57
58 protected:
59  base::MessageLoop message_loop_;
60  base::ScopedTempDir temp_directory_;
61  bool expected_success_;
62  uint64 next_persistent_id_;
63  scoped_ptr<base::RunLoop> run_loop_;
64};
65
66GCMStoreImplTest::GCMStoreImplTest()
67    : expected_success_(true),
68      next_persistent_id_(base::Time::Now().ToInternalValue()) {
69  EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
70  run_loop_.reset(new base::RunLoop());
71}
72
73GCMStoreImplTest::~GCMStoreImplTest() {}
74
75scoped_ptr<GCMStore> GCMStoreImplTest::BuildGCMStore() {
76  return scoped_ptr<GCMStore>(new GCMStoreImpl(
77      temp_directory_.path(),
78      message_loop_.message_loop_proxy(),
79      make_scoped_ptr<Encryptor>(new FakeEncryptor)));
80}
81
82std::string GCMStoreImplTest::GetNextPersistentId() {
83  return base::Uint64ToString(next_persistent_id_++);
84}
85
86void GCMStoreImplTest::PumpLoop() { message_loop_.RunUntilIdle(); }
87
88void GCMStoreImplTest::LoadCallback(
89    scoped_ptr<GCMStore::LoadResult>* result_dst,
90    scoped_ptr<GCMStore::LoadResult> result) {
91  ASSERT_TRUE(result->success);
92  *result_dst = result.Pass();
93  run_loop_->Quit();
94  run_loop_.reset(new base::RunLoop());
95}
96
97void GCMStoreImplTest::UpdateCallback(bool success) {
98  ASSERT_EQ(expected_success_, success);
99}
100
101// Verify creating a new database and loading it.
102TEST_F(GCMStoreImplTest, LoadNew) {
103  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
104  scoped_ptr<GCMStore::LoadResult> load_result;
105  gcm_store->Load(base::Bind(
106      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
107  PumpLoop();
108
109  EXPECT_EQ(0U, load_result->device_android_id);
110  EXPECT_EQ(0U, load_result->device_security_token);
111  EXPECT_TRUE(load_result->incoming_messages.empty());
112  EXPECT_TRUE(load_result->outgoing_messages.empty());
113  EXPECT_TRUE(load_result->gservices_settings.empty());
114  EXPECT_EQ(base::Time::FromInternalValue(0LL), load_result->last_checkin_time);
115}
116
117TEST_F(GCMStoreImplTest, DeviceCredentials) {
118  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
119  scoped_ptr<GCMStore::LoadResult> load_result;
120  gcm_store->Load(base::Bind(
121      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
122  PumpLoop();
123
124  gcm_store->SetDeviceCredentials(
125      kDeviceId,
126      kDeviceToken,
127      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
128  PumpLoop();
129
130  gcm_store = BuildGCMStore().Pass();
131  gcm_store->Load(base::Bind(
132      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
133  PumpLoop();
134
135  ASSERT_EQ(kDeviceId, load_result->device_android_id);
136  ASSERT_EQ(kDeviceToken, load_result->device_security_token);
137}
138
139TEST_F(GCMStoreImplTest, LastCheckinInfo) {
140  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
141  scoped_ptr<GCMStore::LoadResult> load_result;
142  gcm_store->Load(base::Bind(
143      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
144  PumpLoop();
145
146  base::Time last_checkin_time = base::Time::Now();
147  std::set<std::string> accounts;
148  accounts.insert("test_user1@gmail.com");
149  accounts.insert("test_user2@gmail.com");
150
151  gcm_store->SetLastCheckinInfo(
152      last_checkin_time,
153      accounts,
154      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
155  PumpLoop();
156
157  gcm_store = BuildGCMStore().Pass();
158  gcm_store->Load(base::Bind(
159      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
160  PumpLoop();
161  ASSERT_EQ(last_checkin_time, load_result->last_checkin_time);
162  ASSERT_EQ(accounts, load_result->last_checkin_accounts);
163}
164
165TEST_F(GCMStoreImplTest, GServicesSettings_ProtocolV2) {
166  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
167  scoped_ptr<GCMStore::LoadResult> load_result;
168  gcm_store->Load(base::Bind(
169      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
170  PumpLoop();
171
172  std::map<std::string, std::string> settings;
173  settings["checkin_interval"] = "12345";
174  settings["mcs_port"] = "438";
175  settings["checkin_url"] = "http://checkin.google.com";
176  std::string digest = "digest1";
177
178  gcm_store->SetGServicesSettings(
179      settings,
180      digest,
181      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
182  PumpLoop();
183
184  gcm_store = BuildGCMStore().Pass();
185  gcm_store->Load(base::Bind(
186      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
187  PumpLoop();
188
189  ASSERT_EQ(settings, load_result->gservices_settings);
190  ASSERT_EQ(digest, load_result->gservices_digest);
191
192  // Remove some, and add some.
193  settings.clear();
194  settings["checkin_interval"] = "54321";
195  settings["registration_url"] = "http://registration.google.com";
196  digest = "digest2";
197
198  gcm_store->SetGServicesSettings(
199      settings,
200      digest,
201      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
202  PumpLoop();
203
204  gcm_store = BuildGCMStore().Pass();
205  gcm_store->Load(base::Bind(
206      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
207  PumpLoop();
208
209  ASSERT_EQ(settings, load_result->gservices_settings);
210  ASSERT_EQ(digest, load_result->gservices_digest);
211}
212
213TEST_F(GCMStoreImplTest, Registrations) {
214  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
215  scoped_ptr<GCMStore::LoadResult> load_result;
216  gcm_store->Load(base::Bind(
217      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
218  PumpLoop();
219
220  // Add one registration with one sender.
221  linked_ptr<RegistrationInfo> registration1(new RegistrationInfo);
222  registration1->sender_ids.push_back("sender1");
223  registration1->registration_id = "registration1";
224  gcm_store->AddRegistration(
225      "app1",
226      registration1,
227      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
228  PumpLoop();
229
230  // Add one registration with multiple senders.
231  linked_ptr<RegistrationInfo> registration2(new RegistrationInfo);
232  registration2->sender_ids.push_back("sender2_1");
233  registration2->sender_ids.push_back("sender2_2");
234  registration2->registration_id = "registration2";
235  gcm_store->AddRegistration(
236      "app2",
237      registration2,
238      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
239  PumpLoop();
240
241  gcm_store = BuildGCMStore().Pass();
242  gcm_store->Load(base::Bind(
243      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
244  PumpLoop();
245
246  ASSERT_EQ(2u, load_result->registrations.size());
247  ASSERT_TRUE(load_result->registrations.find("app1") !=
248              load_result->registrations.end());
249  EXPECT_EQ(registration1->registration_id,
250            load_result->registrations["app1"]->registration_id);
251  ASSERT_EQ(1u, load_result->registrations["app1"]->sender_ids.size());
252  EXPECT_EQ(registration1->sender_ids[0],
253            load_result->registrations["app1"]->sender_ids[0]);
254  ASSERT_TRUE(load_result->registrations.find("app2") !=
255              load_result->registrations.end());
256  EXPECT_EQ(registration2->registration_id,
257            load_result->registrations["app2"]->registration_id);
258  ASSERT_EQ(2u, load_result->registrations["app2"]->sender_ids.size());
259  EXPECT_EQ(registration2->sender_ids[0],
260            load_result->registrations["app2"]->sender_ids[0]);
261  EXPECT_EQ(registration2->sender_ids[1],
262            load_result->registrations["app2"]->sender_ids[1]);
263
264  gcm_store->RemoveRegistration(
265      "app2",
266      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
267  PumpLoop();
268
269  gcm_store = BuildGCMStore().Pass();
270  gcm_store->Load(base::Bind(
271      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
272  PumpLoop();
273
274  ASSERT_EQ(1u, load_result->registrations.size());
275  ASSERT_TRUE(load_result->registrations.find("app1") !=
276              load_result->registrations.end());
277  EXPECT_EQ(registration1->registration_id,
278            load_result->registrations["app1"]->registration_id);
279  ASSERT_EQ(1u, load_result->registrations["app1"]->sender_ids.size());
280  EXPECT_EQ(registration1->sender_ids[0],
281            load_result->registrations["app1"]->sender_ids[0]);
282}
283
284// Verify saving some incoming messages, reopening the directory, and then
285// removing those incoming messages.
286TEST_F(GCMStoreImplTest, IncomingMessages) {
287  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
288  scoped_ptr<GCMStore::LoadResult> load_result;
289  gcm_store->Load(base::Bind(
290      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
291  PumpLoop();
292
293  std::vector<std::string> persistent_ids;
294  for (int i = 0; i < kNumPersistentIds; ++i) {
295    persistent_ids.push_back(GetNextPersistentId());
296    gcm_store->AddIncomingMessage(
297        persistent_ids.back(),
298        base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
299    PumpLoop();
300  }
301
302  gcm_store = BuildGCMStore().Pass();
303  gcm_store->Load(base::Bind(
304      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
305  PumpLoop();
306
307  ASSERT_EQ(persistent_ids, load_result->incoming_messages);
308  ASSERT_TRUE(load_result->outgoing_messages.empty());
309
310  gcm_store->RemoveIncomingMessages(
311      persistent_ids,
312      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
313  PumpLoop();
314
315  gcm_store = BuildGCMStore().Pass();
316  load_result->incoming_messages.clear();
317  gcm_store->Load(base::Bind(
318      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
319  PumpLoop();
320
321  ASSERT_TRUE(load_result->incoming_messages.empty());
322  ASSERT_TRUE(load_result->outgoing_messages.empty());
323}
324
325// Verify saving some outgoing messages, reopening the directory, and then
326// removing those outgoing messages.
327TEST_F(GCMStoreImplTest, OutgoingMessages) {
328  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
329  scoped_ptr<GCMStore::LoadResult> load_result;
330  gcm_store->Load(base::Bind(
331      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
332  PumpLoop();
333
334  std::vector<std::string> persistent_ids;
335  const int kNumPersistentIds = 10;
336  for (int i = 0; i < kNumPersistentIds; ++i) {
337    persistent_ids.push_back(GetNextPersistentId());
338    mcs_proto::DataMessageStanza message;
339    message.set_from(kAppName + persistent_ids.back());
340    message.set_category(kCategoryName + persistent_ids.back());
341    gcm_store->AddOutgoingMessage(
342        persistent_ids.back(),
343        MCSMessage(message),
344        base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
345    PumpLoop();
346  }
347
348  gcm_store = BuildGCMStore().Pass();
349  gcm_store->Load(base::Bind(
350      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
351  PumpLoop();
352
353  ASSERT_TRUE(load_result->incoming_messages.empty());
354  ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
355  for (int i = 0; i < kNumPersistentIds; ++i) {
356    std::string id = persistent_ids[i];
357    ASSERT_TRUE(load_result->outgoing_messages[id].get());
358    const mcs_proto::DataMessageStanza* message =
359        reinterpret_cast<mcs_proto::DataMessageStanza*>(
360            load_result->outgoing_messages[id].get());
361    ASSERT_EQ(message->from(), kAppName + id);
362    ASSERT_EQ(message->category(), kCategoryName + id);
363  }
364
365  gcm_store->RemoveOutgoingMessages(
366      persistent_ids,
367      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
368  PumpLoop();
369
370  gcm_store = BuildGCMStore().Pass();
371  load_result->outgoing_messages.clear();
372  gcm_store->Load(base::Bind(
373      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
374  PumpLoop();
375
376  ASSERT_TRUE(load_result->incoming_messages.empty());
377  ASSERT_TRUE(load_result->outgoing_messages.empty());
378}
379
380// Verify incoming and outgoing messages don't conflict.
381TEST_F(GCMStoreImplTest, IncomingAndOutgoingMessages) {
382  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
383  scoped_ptr<GCMStore::LoadResult> load_result;
384  gcm_store->Load(base::Bind(
385      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
386  PumpLoop();
387
388  std::vector<std::string> persistent_ids;
389  const int kNumPersistentIds = 10;
390  for (int i = 0; i < kNumPersistentIds; ++i) {
391    persistent_ids.push_back(GetNextPersistentId());
392    gcm_store->AddIncomingMessage(
393        persistent_ids.back(),
394        base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
395    PumpLoop();
396
397    mcs_proto::DataMessageStanza message;
398    message.set_from(kAppName + persistent_ids.back());
399    message.set_category(kCategoryName + persistent_ids.back());
400    gcm_store->AddOutgoingMessage(
401        persistent_ids.back(),
402        MCSMessage(message),
403        base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
404    PumpLoop();
405  }
406
407  gcm_store = BuildGCMStore().Pass();
408  gcm_store->Load(base::Bind(
409      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
410  PumpLoop();
411
412  ASSERT_EQ(persistent_ids, load_result->incoming_messages);
413  ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
414  for (int i = 0; i < kNumPersistentIds; ++i) {
415    std::string id = persistent_ids[i];
416    ASSERT_TRUE(load_result->outgoing_messages[id].get());
417    const mcs_proto::DataMessageStanza* message =
418        reinterpret_cast<mcs_proto::DataMessageStanza*>(
419            load_result->outgoing_messages[id].get());
420    ASSERT_EQ(message->from(), kAppName + id);
421    ASSERT_EQ(message->category(), kCategoryName + id);
422  }
423
424  gcm_store->RemoveIncomingMessages(
425      persistent_ids,
426      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
427  PumpLoop();
428  gcm_store->RemoveOutgoingMessages(
429      persistent_ids,
430      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
431  PumpLoop();
432
433  gcm_store = BuildGCMStore().Pass();
434  load_result->incoming_messages.clear();
435  load_result->outgoing_messages.clear();
436  gcm_store->Load(base::Bind(
437      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
438  PumpLoop();
439
440  ASSERT_TRUE(load_result->incoming_messages.empty());
441  ASSERT_TRUE(load_result->outgoing_messages.empty());
442}
443
444// Test that per-app message limits are enforced, persisted across restarts,
445// and updated as messages are removed.
446TEST_F(GCMStoreImplTest, PerAppMessageLimits) {
447  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
448  scoped_ptr<GCMStore::LoadResult> load_result;
449  gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
450                             base::Unretained(this),
451                             &load_result));
452
453  // Add the initial (below app limit) messages.
454  for (int i = 0; i < kNumMessagesPerApp; ++i) {
455    mcs_proto::DataMessageStanza message;
456    message.set_from(kAppName);
457    message.set_category(kCategoryName);
458    EXPECT_TRUE(gcm_store->AddOutgoingMessage(
459                    base::IntToString(i),
460                    MCSMessage(message),
461                    base::Bind(&GCMStoreImplTest::UpdateCallback,
462                               base::Unretained(this))));
463    PumpLoop();
464  }
465
466  // Attempting to add some more should fail.
467  for (int i = 0; i < kNumMessagesPerApp; ++i) {
468    mcs_proto::DataMessageStanza message;
469    message.set_from(kAppName);
470    message.set_category(kCategoryName);
471    EXPECT_FALSE(gcm_store->AddOutgoingMessage(
472                     base::IntToString(i + kNumMessagesPerApp),
473                     MCSMessage(message),
474                     base::Bind(&GCMStoreImplTest::UpdateCallback,
475                                base::Unretained(this))));
476    PumpLoop();
477  }
478
479  // Tear down and restore the database.
480  gcm_store = BuildGCMStore().Pass();
481  gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
482                             base::Unretained(this),
483                             &load_result));
484  PumpLoop();
485
486  // Adding more messages should still fail.
487  for (int i = 0; i < kNumMessagesPerApp; ++i) {
488    mcs_proto::DataMessageStanza message;
489    message.set_from(kAppName);
490    message.set_category(kCategoryName);
491    EXPECT_FALSE(gcm_store->AddOutgoingMessage(
492                     base::IntToString(i + kNumMessagesPerApp),
493                     MCSMessage(message),
494                     base::Bind(&GCMStoreImplTest::UpdateCallback,
495                                base::Unretained(this))));
496    PumpLoop();
497  }
498
499  // Remove the existing messages.
500  for (int i = 0; i < kNumMessagesPerApp; ++i) {
501    gcm_store->RemoveOutgoingMessage(
502        base::IntToString(i),
503        base::Bind(&GCMStoreImplTest::UpdateCallback,
504                   base::Unretained(this)));
505    PumpLoop();
506  }
507
508  // Successfully add new messages.
509  for (int i = 0; i < kNumMessagesPerApp; ++i) {
510    mcs_proto::DataMessageStanza message;
511    message.set_from(kAppName);
512    message.set_category(kCategoryName);
513    EXPECT_TRUE(gcm_store->AddOutgoingMessage(
514                    base::IntToString(i + kNumMessagesPerApp),
515                    MCSMessage(message),
516                    base::Bind(&GCMStoreImplTest::UpdateCallback,
517                               base::Unretained(this))));
518    PumpLoop();
519  }
520}
521
522TEST_F(GCMStoreImplTest, AccountMapping) {
523  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
524  scoped_ptr<GCMStore::LoadResult> load_result;
525  gcm_store->Load(base::Bind(
526      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
527
528  // Add account mappings.
529  AccountMapping account_mapping1;
530  account_mapping1.account_id = "account_id_1";
531  account_mapping1.email = "account_id_1@gmail.com";
532  account_mapping1.access_token = "account_token1";
533  account_mapping1.status = AccountMapping::ADDING;
534  account_mapping1.status_change_timestamp = base::Time();
535  account_mapping1.last_message_id = "message_1";
536
537  AccountMapping account_mapping2;
538  account_mapping2.account_id = "account_id_2";
539  account_mapping2.email = "account_id_2@gmail.com";
540  account_mapping2.access_token = "account_token1";
541  account_mapping2.status = AccountMapping::REMOVING;
542  account_mapping2.status_change_timestamp =
543      base::Time::FromInternalValue(1305734521259935LL);
544  account_mapping2.last_message_id = "message_2";
545
546  gcm_store->AddAccountMapping(
547      account_mapping1,
548      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
549  PumpLoop();
550  gcm_store->AddAccountMapping(
551      account_mapping2,
552      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
553  PumpLoop();
554
555  gcm_store = BuildGCMStore().Pass();
556  gcm_store->Load(base::Bind(
557      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
558  PumpLoop();
559
560  EXPECT_EQ(2UL, load_result->account_mappings.size());
561  GCMStore::AccountMappings::iterator iter =
562      load_result->account_mappings.begin();
563  EXPECT_EQ(account_mapping1.account_id, iter->account_id);
564  EXPECT_EQ(account_mapping1.email, iter->email);
565  EXPECT_TRUE(iter->access_token.empty());
566  EXPECT_EQ(AccountMapping::ADDING, iter->status);
567  EXPECT_EQ(account_mapping1.status_change_timestamp,
568            iter->status_change_timestamp);
569  EXPECT_EQ(account_mapping1.last_message_id, iter->last_message_id);
570  ++iter;
571  EXPECT_EQ(account_mapping2.account_id, iter->account_id);
572  EXPECT_EQ(account_mapping2.email, iter->email);
573  EXPECT_TRUE(iter->access_token.empty());
574  EXPECT_EQ(AccountMapping::REMOVING, iter->status);
575  EXPECT_EQ(account_mapping2.status_change_timestamp,
576            iter->status_change_timestamp);
577  EXPECT_EQ(account_mapping2.last_message_id, iter->last_message_id);
578
579  gcm_store->RemoveAccountMapping(
580      account_mapping1.account_id,
581      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
582  PumpLoop();
583
584  gcm_store = BuildGCMStore().Pass();
585  gcm_store->Load(base::Bind(
586      &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result));
587  PumpLoop();
588
589  EXPECT_EQ(1UL, load_result->account_mappings.size());
590  iter = load_result->account_mappings.begin();
591  EXPECT_EQ(account_mapping2.account_id, iter->account_id);
592  EXPECT_EQ(account_mapping2.email, iter->email);
593  EXPECT_TRUE(iter->access_token.empty());
594  EXPECT_EQ(AccountMapping::REMOVING, iter->status);
595  EXPECT_EQ(account_mapping2.status_change_timestamp,
596            iter->status_change_timestamp);
597  EXPECT_EQ(account_mapping2.last_message_id, iter->last_message_id);
598}
599
600// When the database is destroyed, all database updates should fail. At the
601// same time, they per-app message counts should not go up, as failures should
602// result in decrementing the counts.
603TEST_F(GCMStoreImplTest, AddMessageAfterDestroy) {
604  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
605  scoped_ptr<GCMStore::LoadResult> load_result;
606  gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
607                             base::Unretained(this),
608                             &load_result));
609  PumpLoop();
610  gcm_store->Destroy(base::Bind(&GCMStoreImplTest::UpdateCallback,
611                               base::Unretained(this)));
612  PumpLoop();
613
614  expected_success_ = false;
615  for (int i = 0; i < kNumMessagesPerApp * 2; ++i) {
616    mcs_proto::DataMessageStanza message;
617    message.set_from(kAppName);
618    message.set_category(kCategoryName);
619    // Because all adds are failing, none should hit the per-app message limits.
620    EXPECT_TRUE(gcm_store->AddOutgoingMessage(
621                    base::IntToString(i),
622                    MCSMessage(message),
623                    base::Bind(&GCMStoreImplTest::UpdateCallback,
624                               base::Unretained(this))));
625    PumpLoop();
626  }
627}
628
629TEST_F(GCMStoreImplTest, ReloadAfterClose) {
630  scoped_ptr<GCMStore> gcm_store(BuildGCMStore());
631  scoped_ptr<GCMStore::LoadResult> load_result;
632  gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
633                             base::Unretained(this),
634                             &load_result));
635  PumpLoop();
636
637  gcm_store->Close();
638  PumpLoop();
639
640  gcm_store->Load(base::Bind(&GCMStoreImplTest::LoadCallback,
641                             base::Unretained(this),
642                             &load_result));
643  PumpLoop();
644}
645
646}  // namespace
647
648}  // namespace gcm
649