Home | History | Annotate | Download | only in engine
      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   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 
     66 GCMStoreImplTest::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 
     73 GCMStoreImplTest::~GCMStoreImplTest() {}
     74 
     75 scoped_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 
     82 std::string GCMStoreImplTest::GetNextPersistentId() {
     83   return base::Uint64ToString(next_persistent_id_++);
     84 }
     85 
     86 void GCMStoreImplTest::PumpLoop() { message_loop_.RunUntilIdle(); }
     87 
     88 void 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 
     97 void GCMStoreImplTest::UpdateCallback(bool success) {
     98   ASSERT_EQ(expected_success_, success);
     99 }
    100 
    101 // Verify creating a new database and loading it.
    102 TEST_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 
    117 TEST_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 
    139 TEST_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 (at) gmail.com");
    149   accounts.insert("test_user2 (at) 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 
    165 TEST_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 
    213 TEST_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.
    286 TEST_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.
    327 TEST_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.
    381 TEST_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.
    446 TEST_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 
    522 TEST_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 (at) 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 (at) 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.
    603 TEST_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 
    629 TEST_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