gcm_store_impl_unittest.cc 25.8 KB
Newer Older
1 2 3 4 5 6
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "google_apis/gcm/engine/gcm_store_impl.h"

7
#include <stdint.h>
8 9

#include <memory>
10
#include <string>
11
#include <utility>
12 13 14
#include <vector>

#include "base/bind.h"
15
#include "base/command_line.h"
16 17
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
18
#include "base/memory/ptr_util.h"
19
#include "base/strings/string_number_conversions.h"
20
#include "base/test/test_simple_task_runner.h"
21
#include "base/threading/thread_task_runner_handle.h"
22
#include "google_apis/gcm/base/fake_encryptor.h"
23 24 25 26 27 28 29 30 31 32 33 34
#include "google_apis/gcm/base/mcs_message.h"
#include "google_apis/gcm/base/mcs_util.h"
#include "google_apis/gcm/protocol/mcs.pb.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace gcm {

namespace {

// Number of persistent ids to use in tests.
const int kNumPersistentIds = 10;

35 36 37 38 39
// Number of per-app messages in tests.
const int kNumMessagesPerApp = 20;

// App name for testing.
const char kAppName[] = "my_app";
40
const char kAppName2[] = "my_app_2";
41 42 43 44

// Category name for testing.
const char kCategoryName[] = "my_category";

45 46
const uint64_t kDeviceId = 22;
const uint64_t kDeviceToken = 55;
47 48 49 50

class GCMStoreImplTest : public testing::Test {
 public:
  GCMStoreImplTest();
51
  ~GCMStoreImplTest() override;
52

53 54 55
  std::unique_ptr<GCMStoreImpl> BuildGCMStore();
  void LoadGCMStore(GCMStoreImpl* gcm_store,
                    std::unique_ptr<GCMStore::LoadResult>* result_dst);
56 57 58 59 60

  std::string GetNextPersistentId();

  void PumpLoop();

61 62 63 64 65
  void LoadCallback(std::unique_ptr<GCMStore::LoadResult>* result_dst,
                    std::unique_ptr<GCMStore::LoadResult> result);
  void LoadWithoutCheckCallback(
      std::unique_ptr<GCMStore::LoadResult>* result_dst,
      std::unique_ptr<GCMStore::LoadResult> result);
66 67
  void UpdateCallback(bool success);

68
 protected:
69 70
  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
  base::ThreadTaskRunnerHandle task_runner_handle_;
71
  base::ScopedTempDir temp_directory_;
72
  bool expected_success_;
73
  uint64_t next_persistent_id_;
74 75
};

76
GCMStoreImplTest::GCMStoreImplTest()
77 78 79
    : task_runner_(new base::TestSimpleTaskRunner()),
      task_runner_handle_(task_runner_),
      expected_success_(true),
80
      next_persistent_id_(base::Time::Now().ToInternalValue()) {
81 82 83 84 85
  EXPECT_TRUE(temp_directory_.CreateUniqueTempDir());
}

GCMStoreImplTest::~GCMStoreImplTest() {}

86 87
std::unique_ptr<GCMStoreImpl> GCMStoreImplTest::BuildGCMStore() {
  return std::unique_ptr<GCMStoreImpl>(new GCMStoreImpl(
88 89 90
      // Pass an non-existent directory as store path to match the exact
      // behavior in the production code. Currently GCMStoreImpl checks if
      // the directory exist or not to determine the store existence.
91
      temp_directory_.GetPath().Append(FILE_PATH_LITERAL("GCM Store")),
92
      task_runner_, base::WrapUnique<Encryptor>(new FakeEncryptor)));
93 94
}

95
void GCMStoreImplTest::LoadGCMStore(
96 97
    GCMStoreImpl* gcm_store,
    std::unique_ptr<GCMStore::LoadResult>* result_dst) {
98 99 100 101 102 103 104 105
  gcm_store->Load(
      GCMStore::CREATE_IF_MISSING,
      base::Bind(&GCMStoreImplTest::LoadCallback,
                 base::Unretained(this),
                 result_dst));
  PumpLoop();
}

106
std::string GCMStoreImplTest::GetNextPersistentId() {
107
  return base::Uint64ToString(next_persistent_id_++);
108 109
}

110
void GCMStoreImplTest::PumpLoop() { task_runner_->RunUntilIdle(); }
111

zea@chromium.org's avatar
zea@chromium.org committed
112
void GCMStoreImplTest::LoadCallback(
113 114
    std::unique_ptr<GCMStore::LoadResult>* result_dst,
    std::unique_ptr<GCMStore::LoadResult> result) {
zea@chromium.org's avatar
zea@chromium.org committed
115
  ASSERT_TRUE(result->success);
116
  LoadWithoutCheckCallback(result_dst, std::move(result));
117 118 119
}

void GCMStoreImplTest::LoadWithoutCheckCallback(
120 121
    std::unique_ptr<GCMStore::LoadResult>* result_dst,
    std::unique_ptr<GCMStore::LoadResult> result) {
122
  *result_dst = std::move(result);
123 124
}

125 126 127
void GCMStoreImplTest::UpdateCallback(bool success) {
  ASSERT_EQ(expected_success_, success);
}
128 129 130

// Verify creating a new database and loading it.
TEST_F(GCMStoreImplTest, LoadNew) {
131 132
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
133
  LoadGCMStore(gcm_store.get(), &load_result);
134

zea@chromium.org's avatar
zea@chromium.org committed
135 136 137 138
  EXPECT_EQ(0U, load_result->device_android_id);
  EXPECT_EQ(0U, load_result->device_security_token);
  EXPECT_TRUE(load_result->incoming_messages.empty());
  EXPECT_TRUE(load_result->outgoing_messages.empty());
139
  EXPECT_TRUE(load_result->gservices_settings.empty());
140
  EXPECT_EQ(base::Time::FromInternalValue(0LL), load_result->last_checkin_time);
141 142
}

143 144
// Verify new database is not created when DO_NOT_CREATE_NEW_STORE is passed.
TEST_F(GCMStoreImplTest, LoadWithoutCreatingNewStore) {
145 146
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
147 148 149 150 151
  gcm_store->Load(
      GCMStore::DO_NOT_CREATE,
      base::Bind(&GCMStoreImplTest::LoadWithoutCheckCallback,
                 base::Unretained(this),
                 &load_result));
152 153
  PumpLoop();

154 155 156 157 158
  EXPECT_FALSE(load_result->success);
  EXPECT_TRUE(load_result->store_does_not_exist);
}

TEST_F(GCMStoreImplTest, DeviceCredentials) {
159 160
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
161 162
  LoadGCMStore(gcm_store.get(), &load_result);

163 164 165 166 167 168
  gcm_store->SetDeviceCredentials(
      kDeviceId,
      kDeviceToken,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

169
  gcm_store = BuildGCMStore();
170
  LoadGCMStore(gcm_store.get(), &load_result);
171

zea@chromium.org's avatar
zea@chromium.org committed
172 173
  ASSERT_EQ(kDeviceId, load_result->device_android_id);
  ASSERT_EQ(kDeviceToken, load_result->device_security_token);
174 175
}

176
TEST_F(GCMStoreImplTest, LastCheckinInfo) {
177 178
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
179
  LoadGCMStore(gcm_store.get(), &load_result);
180 181

  base::Time last_checkin_time = base::Time::Now();
182 183 184
  std::set<std::string> accounts;
  accounts.insert("test_user1@gmail.com");
  accounts.insert("test_user2@gmail.com");
185

186
  gcm_store->SetLastCheckinInfo(
187
      last_checkin_time,
188
      accounts,
189 190 191
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

192
  gcm_store = BuildGCMStore();
193
  LoadGCMStore(gcm_store.get(), &load_result);
194
  ASSERT_EQ(last_checkin_time, load_result->last_checkin_time);
195
  ASSERT_EQ(accounts, load_result->last_checkin_accounts);
196 197 198 199 200 201 202 203

  // Negative cases, where the value read is gibberish.
  gcm_store->SetValueForTesting(
      "last_checkin_time",
      "gibberish",
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

204
  gcm_store = BuildGCMStore();
205
  LoadGCMStore(gcm_store.get(), &load_result);
206
  EXPECT_EQ(base::Time(), load_result->last_checkin_time);
207 208
}

209
TEST_F(GCMStoreImplTest, GServicesSettings_ProtocolV2) {
210 211
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
212
  LoadGCMStore(gcm_store.get(), &load_result);
213 214 215 216 217 218 219 220 221 222 223 224 225

  std::map<std::string, std::string> settings;
  settings["checkin_interval"] = "12345";
  settings["mcs_port"] = "438";
  settings["checkin_url"] = "http://checkin.google.com";
  std::string digest = "digest1";

  gcm_store->SetGServicesSettings(
      settings,
      digest,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

226
  gcm_store = BuildGCMStore();
227
  LoadGCMStore(gcm_store.get(), &load_result);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243

  ASSERT_EQ(settings, load_result->gservices_settings);
  ASSERT_EQ(digest, load_result->gservices_digest);

  // Remove some, and add some.
  settings.clear();
  settings["checkin_interval"] = "54321";
  settings["registration_url"] = "http://registration.google.com";
  digest = "digest2";

  gcm_store->SetGServicesSettings(
      settings,
      digest,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

244
  gcm_store = BuildGCMStore();
245
  LoadGCMStore(gcm_store.get(), &load_result);
246 247 248 249 250

  ASSERT_EQ(settings, load_result->gservices_settings);
  ASSERT_EQ(digest, load_result->gservices_digest);
}

251
TEST_F(GCMStoreImplTest, Registrations) {
252 253
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
254
  LoadGCMStore(gcm_store.get(), &load_result);
255 256

  // Add one registration with one sender.
257
  std::string registration = "sender1=registration1";
258
  gcm_store->AddRegistration(
259 260
      kAppName,
      registration,
261 262 263 264
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

  // Add one registration with multiple senders.
265
  std::string registration2 = "sender1,sender2=registration2";
266
  gcm_store->AddRegistration(
267
      kAppName2,
268 269 270 271
      registration2,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

272
  gcm_store = BuildGCMStore();
273
  LoadGCMStore(gcm_store.get(), &load_result);
274

275
  ASSERT_EQ(2u, load_result->registrations.size());
276
  ASSERT_TRUE(load_result->registrations.find(kAppName) !=
277
              load_result->registrations.end());
278 279
  EXPECT_EQ(registration, load_result->registrations[kAppName]);
  ASSERT_TRUE(load_result->registrations.find(kAppName2) !=
280
              load_result->registrations.end());
281
  EXPECT_EQ(registration2, load_result->registrations[kAppName2]);
282 283

  gcm_store->RemoveRegistration(
284
      kAppName2,
285 286 287
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

288
  gcm_store = BuildGCMStore();
289
  LoadGCMStore(gcm_store.get(), &load_result);
290 291

  ASSERT_EQ(1u, load_result->registrations.size());
292
  ASSERT_TRUE(load_result->registrations.find(kAppName) !=
293
              load_result->registrations.end());
294
  EXPECT_EQ(registration, load_result->registrations[kAppName]);
295 296
}

297 298 299
// Verify saving some incoming messages, reopening the directory, and then
// removing those incoming messages.
TEST_F(GCMStoreImplTest, IncomingMessages) {
300 301
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
302
  LoadGCMStore(gcm_store.get(), &load_result);
303 304 305 306 307 308 309 310 311 312

  std::vector<std::string> persistent_ids;
  for (int i = 0; i < kNumPersistentIds; ++i) {
    persistent_ids.push_back(GetNextPersistentId());
    gcm_store->AddIncomingMessage(
        persistent_ids.back(),
        base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
    PumpLoop();
  }

313
  gcm_store = BuildGCMStore();
314
  LoadGCMStore(gcm_store.get(), &load_result);
315

zea@chromium.org's avatar
zea@chromium.org committed
316 317
  ASSERT_EQ(persistent_ids, load_result->incoming_messages);
  ASSERT_TRUE(load_result->outgoing_messages.empty());
318 319 320 321 322 323

  gcm_store->RemoveIncomingMessages(
      persistent_ids,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

324
  gcm_store = BuildGCMStore();
zea@chromium.org's avatar
zea@chromium.org committed
325
  load_result->incoming_messages.clear();
326
  LoadGCMStore(gcm_store.get(), &load_result);
327

zea@chromium.org's avatar
zea@chromium.org committed
328 329
  ASSERT_TRUE(load_result->incoming_messages.empty());
  ASSERT_TRUE(load_result->outgoing_messages.empty());
330 331 332 333
}

// Verify saving some outgoing messages, reopening the directory, and then
// removing those outgoing messages.
zea@chromium.org's avatar
zea@chromium.org committed
334
TEST_F(GCMStoreImplTest, OutgoingMessages) {
335 336
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
337
  LoadGCMStore(gcm_store.get(), &load_result);
338 339 340 341 342 343

  std::vector<std::string> persistent_ids;
  const int kNumPersistentIds = 10;
  for (int i = 0; i < kNumPersistentIds; ++i) {
    persistent_ids.push_back(GetNextPersistentId());
    mcs_proto::DataMessageStanza message;
344 345
    message.set_from(kAppName + persistent_ids.back());
    message.set_category(kCategoryName + persistent_ids.back());
346 347 348 349 350 351 352
    gcm_store->AddOutgoingMessage(
        persistent_ids.back(),
        MCSMessage(message),
        base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
    PumpLoop();
  }

353
  gcm_store = BuildGCMStore();
354
  LoadGCMStore(gcm_store.get(), &load_result);
355

zea@chromium.org's avatar
zea@chromium.org committed
356 357
  ASSERT_TRUE(load_result->incoming_messages.empty());
  ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
358 359
  for (int i = 0; i < kNumPersistentIds; ++i) {
    std::string id = persistent_ids[i];
zea@chromium.org's avatar
zea@chromium.org committed
360
    ASSERT_TRUE(load_result->outgoing_messages[id].get());
361 362
    const mcs_proto::DataMessageStanza* message =
        reinterpret_cast<mcs_proto::DataMessageStanza*>(
zea@chromium.org's avatar
zea@chromium.org committed
363
            load_result->outgoing_messages[id].get());
364 365
    ASSERT_EQ(message->from(), kAppName + id);
    ASSERT_EQ(message->category(), kCategoryName + id);
366 367 368 369 370 371 372
  }

  gcm_store->RemoveOutgoingMessages(
      persistent_ids,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

373
  gcm_store = BuildGCMStore();
zea@chromium.org's avatar
zea@chromium.org committed
374
  load_result->outgoing_messages.clear();
375
  LoadGCMStore(gcm_store.get(), &load_result);
376

zea@chromium.org's avatar
zea@chromium.org committed
377 378
  ASSERT_TRUE(load_result->incoming_messages.empty());
  ASSERT_TRUE(load_result->outgoing_messages.empty());
379 380 381
}

// Verify incoming and outgoing messages don't conflict.
zea@chromium.org's avatar
zea@chromium.org committed
382
TEST_F(GCMStoreImplTest, IncomingAndOutgoingMessages) {
383 384
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
385
  LoadGCMStore(gcm_store.get(), &load_result);
386 387 388 389 390 391 392 393 394 395 396

  std::vector<std::string> persistent_ids;
  const int kNumPersistentIds = 10;
  for (int i = 0; i < kNumPersistentIds; ++i) {
    persistent_ids.push_back(GetNextPersistentId());
    gcm_store->AddIncomingMessage(
        persistent_ids.back(),
        base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
    PumpLoop();

    mcs_proto::DataMessageStanza message;
397 398
    message.set_from(kAppName + persistent_ids.back());
    message.set_category(kCategoryName + persistent_ids.back());
399 400 401 402 403 404 405
    gcm_store->AddOutgoingMessage(
        persistent_ids.back(),
        MCSMessage(message),
        base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
    PumpLoop();
  }

406
  gcm_store = BuildGCMStore();
407
  LoadGCMStore(gcm_store.get(), &load_result);
408

zea@chromium.org's avatar
zea@chromium.org committed
409 410
  ASSERT_EQ(persistent_ids, load_result->incoming_messages);
  ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size());
411 412
  for (int i = 0; i < kNumPersistentIds; ++i) {
    std::string id = persistent_ids[i];
zea@chromium.org's avatar
zea@chromium.org committed
413
    ASSERT_TRUE(load_result->outgoing_messages[id].get());
414 415
    const mcs_proto::DataMessageStanza* message =
        reinterpret_cast<mcs_proto::DataMessageStanza*>(
zea@chromium.org's avatar
zea@chromium.org committed
416
            load_result->outgoing_messages[id].get());
417 418
    ASSERT_EQ(message->from(), kAppName + id);
    ASSERT_EQ(message->category(), kCategoryName + id);
419 420 421 422 423 424 425 426 427 428 429
  }

  gcm_store->RemoveIncomingMessages(
      persistent_ids,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();
  gcm_store->RemoveOutgoingMessages(
      persistent_ids,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

430
  gcm_store = BuildGCMStore();
zea@chromium.org's avatar
zea@chromium.org committed
431 432
  load_result->incoming_messages.clear();
  load_result->outgoing_messages.clear();
433
  LoadGCMStore(gcm_store.get(), &load_result);
434

zea@chromium.org's avatar
zea@chromium.org committed
435 436
  ASSERT_TRUE(load_result->incoming_messages.empty());
  ASSERT_TRUE(load_result->outgoing_messages.empty());
437 438
}

439 440
// Test that per-app message limits are enforced, persisted across restarts,
// and updated as messages are removed.
zea@chromium.org's avatar
zea@chromium.org committed
441
TEST_F(GCMStoreImplTest, PerAppMessageLimits) {
442 443
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
444
  LoadGCMStore(gcm_store.get(), &load_result);
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472

  // Add the initial (below app limit) messages.
  for (int i = 0; i < kNumMessagesPerApp; ++i) {
    mcs_proto::DataMessageStanza message;
    message.set_from(kAppName);
    message.set_category(kCategoryName);
    EXPECT_TRUE(gcm_store->AddOutgoingMessage(
                    base::IntToString(i),
                    MCSMessage(message),
                    base::Bind(&GCMStoreImplTest::UpdateCallback,
                               base::Unretained(this))));
    PumpLoop();
  }

  // Attempting to add some more should fail.
  for (int i = 0; i < kNumMessagesPerApp; ++i) {
    mcs_proto::DataMessageStanza message;
    message.set_from(kAppName);
    message.set_category(kCategoryName);
    EXPECT_FALSE(gcm_store->AddOutgoingMessage(
                     base::IntToString(i + kNumMessagesPerApp),
                     MCSMessage(message),
                     base::Bind(&GCMStoreImplTest::UpdateCallback,
                                base::Unretained(this))));
    PumpLoop();
  }

  // Tear down and restore the database.
473
  gcm_store = BuildGCMStore();
474
  LoadGCMStore(gcm_store.get(), &load_result);
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

  // Adding more messages should still fail.
  for (int i = 0; i < kNumMessagesPerApp; ++i) {
    mcs_proto::DataMessageStanza message;
    message.set_from(kAppName);
    message.set_category(kCategoryName);
    EXPECT_FALSE(gcm_store->AddOutgoingMessage(
                     base::IntToString(i + kNumMessagesPerApp),
                     MCSMessage(message),
                     base::Bind(&GCMStoreImplTest::UpdateCallback,
                                base::Unretained(this))));
    PumpLoop();
  }

  // Remove the existing messages.
  for (int i = 0; i < kNumMessagesPerApp; ++i) {
    gcm_store->RemoveOutgoingMessage(
        base::IntToString(i),
        base::Bind(&GCMStoreImplTest::UpdateCallback,
                   base::Unretained(this)));
    PumpLoop();
  }

  // Successfully add new messages.
  for (int i = 0; i < kNumMessagesPerApp; ++i) {
    mcs_proto::DataMessageStanza message;
    message.set_from(kAppName);
    message.set_category(kCategoryName);
    EXPECT_TRUE(gcm_store->AddOutgoingMessage(
                    base::IntToString(i + kNumMessagesPerApp),
                    MCSMessage(message),
                    base::Bind(&GCMStoreImplTest::UpdateCallback,
                               base::Unretained(this))));
    PumpLoop();
  }
}

512
TEST_F(GCMStoreImplTest, AccountMapping) {
513 514
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
515
  LoadGCMStore(gcm_store.get(), &load_result);
516 517

  // Add account mappings.
518 519 520 521 522 523 524 525 526 527 528 529 530 531
  AccountMapping account_mapping1;
  account_mapping1.account_id = "account_id_1";
  account_mapping1.email = "account_id_1@gmail.com";
  account_mapping1.access_token = "account_token1";
  account_mapping1.status = AccountMapping::ADDING;
  account_mapping1.status_change_timestamp = base::Time();
  account_mapping1.last_message_id = "message_1";

  AccountMapping account_mapping2;
  account_mapping2.account_id = "account_id_2";
  account_mapping2.email = "account_id_2@gmail.com";
  account_mapping2.access_token = "account_token1";
  account_mapping2.status = AccountMapping::REMOVING;
  account_mapping2.status_change_timestamp =
532
      base::Time::FromInternalValue(1305734521259935LL);
533
  account_mapping2.last_message_id = "message_2";
534 535

  gcm_store->AddAccountMapping(
536
      account_mapping1,
537 538 539
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();
  gcm_store->AddAccountMapping(
540
      account_mapping2,
541 542 543
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

544
  gcm_store = BuildGCMStore();
545
  LoadGCMStore(gcm_store.get(), &load_result);
546

547
  EXPECT_EQ(2UL, load_result->account_mappings.size());
548
  GCMStore::AccountMappings::iterator iter =
549
      load_result->account_mappings.begin();
550 551 552 553
  EXPECT_EQ(account_mapping1.account_id, iter->account_id);
  EXPECT_EQ(account_mapping1.email, iter->email);
  EXPECT_TRUE(iter->access_token.empty());
  EXPECT_EQ(AccountMapping::ADDING, iter->status);
554
  EXPECT_EQ(account_mapping1.status_change_timestamp,
555 556
            iter->status_change_timestamp);
  EXPECT_EQ(account_mapping1.last_message_id, iter->last_message_id);
557
  ++iter;
558 559 560 561
  EXPECT_EQ(account_mapping2.account_id, iter->account_id);
  EXPECT_EQ(account_mapping2.email, iter->email);
  EXPECT_TRUE(iter->access_token.empty());
  EXPECT_EQ(AccountMapping::REMOVING, iter->status);
562
  EXPECT_EQ(account_mapping2.status_change_timestamp,
563 564
            iter->status_change_timestamp);
  EXPECT_EQ(account_mapping2.last_message_id, iter->last_message_id);
565 566

  gcm_store->RemoveAccountMapping(
567
      account_mapping1.account_id,
568 569 570
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

571
  gcm_store = BuildGCMStore();
572
  LoadGCMStore(gcm_store.get(), &load_result);
573

574 575
  EXPECT_EQ(1UL, load_result->account_mappings.size());
  iter = load_result->account_mappings.begin();
576 577 578 579
  EXPECT_EQ(account_mapping2.account_id, iter->account_id);
  EXPECT_EQ(account_mapping2.email, iter->email);
  EXPECT_TRUE(iter->access_token.empty());
  EXPECT_EQ(AccountMapping::REMOVING, iter->status);
580
  EXPECT_EQ(account_mapping2.status_change_timestamp,
581 582
            iter->status_change_timestamp);
  EXPECT_EQ(account_mapping2.last_message_id, iter->last_message_id);
583 584
}

585
TEST_F(GCMStoreImplTest, HeartbeatInterval) {
586 587
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
588
  LoadGCMStore(gcm_store.get(), &load_result);
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605

  std::string scope1 = "scope1";
  std::string scope2 = "scope2";
  int heartbeat1 = 120 * 1000;
  int heartbeat2 = 360 * 1000;

  gcm_store->AddHeartbeatInterval(
      scope1,
      heartbeat1,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();
  gcm_store->AddHeartbeatInterval(
      scope2,
      heartbeat2,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

606
  gcm_store = BuildGCMStore();
607
  LoadGCMStore(gcm_store.get(), &load_result);
608 609 610 611 612 613 614 615 616 617 618 619 620 621

  EXPECT_EQ(2UL, load_result->heartbeat_intervals.size());
  ASSERT_TRUE(load_result->heartbeat_intervals.find(scope1) !=
              load_result->heartbeat_intervals.end());
  EXPECT_EQ(heartbeat1, load_result->heartbeat_intervals[scope1]);
  ASSERT_TRUE(load_result->heartbeat_intervals.find(scope2) !=
              load_result->heartbeat_intervals.end());
  EXPECT_EQ(heartbeat2, load_result->heartbeat_intervals[scope2]);

  gcm_store->RemoveHeartbeatInterval(
      scope2,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

622
  gcm_store = BuildGCMStore();
623
  LoadGCMStore(gcm_store.get(), &load_result);
624 625 626 627 628 629 630

  EXPECT_EQ(1UL, load_result->heartbeat_intervals.size());
  ASSERT_TRUE(load_result->heartbeat_intervals.find(scope1) !=
              load_result->heartbeat_intervals.end());
  EXPECT_EQ(heartbeat1, load_result->heartbeat_intervals[scope1]);
}

631 632 633 634
// When the database is destroyed, all database updates should fail. At the
// same time, they per-app message counts should not go up, as failures should
// result in decrementing the counts.
TEST_F(GCMStoreImplTest, AddMessageAfterDestroy) {
635 636
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
637
  LoadGCMStore(gcm_store.get(), &load_result);
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
  gcm_store->Destroy(base::Bind(&GCMStoreImplTest::UpdateCallback,
                               base::Unretained(this)));
  PumpLoop();

  expected_success_ = false;
  for (int i = 0; i < kNumMessagesPerApp * 2; ++i) {
    mcs_proto::DataMessageStanza message;
    message.set_from(kAppName);
    message.set_category(kCategoryName);
    // Because all adds are failing, none should hit the per-app message limits.
    EXPECT_TRUE(gcm_store->AddOutgoingMessage(
                    base::IntToString(i),
                    MCSMessage(message),
                    base::Bind(&GCMStoreImplTest::UpdateCallback,
                               base::Unretained(this))));
    PumpLoop();
  }
}

657
TEST_F(GCMStoreImplTest, ReloadAfterClose) {
658 659
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
660
  LoadGCMStore(gcm_store.get(), &load_result);
661 662 663 664

  gcm_store->Close();
  PumpLoop();

665
  LoadGCMStore(gcm_store.get(), &load_result);
666 667
}

668
TEST_F(GCMStoreImplTest, LastTokenFetchTime) {
669 670
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
671
  LoadGCMStore(gcm_store.get(), &load_result);
672 673 674 675 676 677 678 679
  EXPECT_EQ(base::Time(), load_result->last_token_fetch_time);

  base::Time last_token_fetch_time = base::Time::Now();
  gcm_store->SetLastTokenFetchTime(
      last_token_fetch_time,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

680
  gcm_store = BuildGCMStore();
681
  LoadGCMStore(gcm_store.get(), &load_result);
682
  EXPECT_EQ(last_token_fetch_time, load_result->last_token_fetch_time);
683 684 685 686 687 688 689 690

  // Negative cases, where the value read is gibberish.
  gcm_store->SetValueForTesting(
      "last_token_fetch_time",
      "gibberish",
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

691
  gcm_store = BuildGCMStore();
692
  LoadGCMStore(gcm_store.get(), &load_result);
693
  EXPECT_EQ(base::Time(), load_result->last_token_fetch_time);
694 695
}

696
TEST_F(GCMStoreImplTest, InstanceIDData) {
697 698
  std::unique_ptr<GCMStoreImpl> gcm_store(BuildGCMStore());
  std::unique_ptr<GCMStore::LoadResult> load_result;
699
  LoadGCMStore(gcm_store.get(), &load_result);
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714

  std::string instance_id_data("Foo");
  gcm_store->AddInstanceIDData(
      kAppName,
      instance_id_data,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

  std::string instance_id_data2("Hello Instance ID");
  gcm_store->AddInstanceIDData(
      kAppName2,
      instance_id_data2,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

715
  gcm_store = BuildGCMStore();
716
  LoadGCMStore(gcm_store.get(), &load_result);
717 718 719 720 721 722 723 724 725 726 727 728 729 730

  ASSERT_EQ(2u, load_result->instance_id_data.size());
  ASSERT_TRUE(load_result->instance_id_data.find(kAppName) !=
              load_result->instance_id_data.end());
  ASSERT_TRUE(load_result->instance_id_data.find(kAppName2) !=
              load_result->instance_id_data.end());
  EXPECT_EQ(instance_id_data, load_result->instance_id_data[kAppName]);
  EXPECT_EQ(instance_id_data2, load_result->instance_id_data[kAppName2]);

  gcm_store->RemoveInstanceIDData(
      kAppName,
      base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this)));
  PumpLoop();

731
  gcm_store = BuildGCMStore();
732
  LoadGCMStore(gcm_store.get(), &load_result);
733 734 735 736 737 738 739

  ASSERT_EQ(1u, load_result->instance_id_data.size());
  ASSERT_TRUE(load_result->instance_id_data.find(kAppName2) !=
              load_result->instance_id_data.end());
  EXPECT_EQ(instance_id_data2, load_result->instance_id_data[kAppName2]);
}

740 741 742
}  // namespace

}  // namespace gcm