// 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" #include #include #include #include "base/bind.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" #include "base/test/test_simple_task_runner.h" #include "base/thread_task_runner_handle.h" #include "google_apis/gcm/base/fake_encryptor.h" #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; // Number of per-app messages in tests. const int kNumMessagesPerApp = 20; // App name for testing. const char kAppName[] = "my_app"; const char kAppName2[] = "my_app_2"; // Category name for testing. const char kCategoryName[] = "my_category"; const uint64_t kDeviceId = 22; const uint64_t kDeviceToken = 55; class GCMStoreImplTest : public testing::Test { public: GCMStoreImplTest(); ~GCMStoreImplTest() override; scoped_ptr BuildGCMStore(); void LoadGCMStore( GCMStoreImpl* gcm_store, scoped_ptr* result_dst); std::string GetNextPersistentId(); void PumpLoop(); void LoadCallback(scoped_ptr* result_dst, scoped_ptr result); void LoadWithoutCheckCallback(scoped_ptr* result_dst, scoped_ptr result); void UpdateCallback(bool success); protected: scoped_refptr task_runner_; base::ThreadTaskRunnerHandle task_runner_handle_; base::ScopedTempDir temp_directory_; bool expected_success_; uint64_t next_persistent_id_; }; GCMStoreImplTest::GCMStoreImplTest() : task_runner_(new base::TestSimpleTaskRunner()), task_runner_handle_(task_runner_), expected_success_(true), next_persistent_id_(base::Time::Now().ToInternalValue()) { EXPECT_TRUE(temp_directory_.CreateUniqueTempDir()); } GCMStoreImplTest::~GCMStoreImplTest() {} scoped_ptr GCMStoreImplTest::BuildGCMStore() { return scoped_ptr(new GCMStoreImpl( // 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. temp_directory_.path().Append(FILE_PATH_LITERAL("GCM Store")), task_runner_, make_scoped_ptr(new FakeEncryptor))); } void GCMStoreImplTest::LoadGCMStore( GCMStoreImpl* gcm_store, scoped_ptr* result_dst) { gcm_store->Load( GCMStore::CREATE_IF_MISSING, base::Bind(&GCMStoreImplTest::LoadCallback, base::Unretained(this), result_dst)); PumpLoop(); } std::string GCMStoreImplTest::GetNextPersistentId() { return base::Uint64ToString(next_persistent_id_++); } void GCMStoreImplTest::PumpLoop() { task_runner_->RunUntilIdle(); } void GCMStoreImplTest::LoadCallback( scoped_ptr* result_dst, scoped_ptr result) { ASSERT_TRUE(result->success); LoadWithoutCheckCallback(result_dst, result.Pass()); } void GCMStoreImplTest::LoadWithoutCheckCallback( scoped_ptr* result_dst, scoped_ptr result) { *result_dst = result.Pass(); } void GCMStoreImplTest::UpdateCallback(bool success) { ASSERT_EQ(expected_success_, success); } // Verify creating a new database and loading it. TEST_F(GCMStoreImplTest, LoadNew) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); 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()); EXPECT_TRUE(load_result->gservices_settings.empty()); EXPECT_EQ(base::Time::FromInternalValue(0LL), load_result->last_checkin_time); } // Verify new database is not created when DO_NOT_CREATE_NEW_STORE is passed. TEST_F(GCMStoreImplTest, LoadWithoutCreatingNewStore) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; gcm_store->Load( GCMStore::DO_NOT_CREATE, base::Bind(&GCMStoreImplTest::LoadWithoutCheckCallback, base::Unretained(this), &load_result)); PumpLoop(); EXPECT_FALSE(load_result->success); EXPECT_TRUE(load_result->store_does_not_exist); } TEST_F(GCMStoreImplTest, DeviceCredentials) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); gcm_store->SetDeviceCredentials( kDeviceId, kDeviceToken, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_EQ(kDeviceId, load_result->device_android_id); ASSERT_EQ(kDeviceToken, load_result->device_security_token); } TEST_F(GCMStoreImplTest, LastCheckinInfo) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); base::Time last_checkin_time = base::Time::Now(); std::set accounts; accounts.insert("test_user1@gmail.com"); accounts.insert("test_user2@gmail.com"); gcm_store->SetLastCheckinInfo( last_checkin_time, accounts, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_EQ(last_checkin_time, load_result->last_checkin_time); ASSERT_EQ(accounts, load_result->last_checkin_accounts); // Negative cases, where the value read is gibberish. gcm_store->SetValueForTesting( "last_checkin_time", "gibberish", base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); EXPECT_EQ(base::Time(), load_result->last_checkin_time); } TEST_F(GCMStoreImplTest, GServicesSettings_ProtocolV2) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); std::map 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(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); 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(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_EQ(settings, load_result->gservices_settings); ASSERT_EQ(digest, load_result->gservices_digest); } TEST_F(GCMStoreImplTest, Registrations) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); // Add one registration with one sender. std::string registration = "sender1=registration1"; gcm_store->AddRegistration( kAppName, registration, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); // Add one registration with multiple senders. std::string registration2 = "sender1,sender2=registration2"; gcm_store->AddRegistration( kAppName2, registration2, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_EQ(2u, load_result->registrations.size()); ASSERT_TRUE(load_result->registrations.find(kAppName) != load_result->registrations.end()); EXPECT_EQ(registration, load_result->registrations[kAppName]); ASSERT_TRUE(load_result->registrations.find(kAppName2) != load_result->registrations.end()); EXPECT_EQ(registration2, load_result->registrations[kAppName2]); gcm_store->RemoveRegistration( kAppName2, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_EQ(1u, load_result->registrations.size()); ASSERT_TRUE(load_result->registrations.find(kAppName) != load_result->registrations.end()); EXPECT_EQ(registration, load_result->registrations[kAppName]); } // Verify saving some incoming messages, reopening the directory, and then // removing those incoming messages. TEST_F(GCMStoreImplTest, IncomingMessages) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); std::vector 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(); } gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_EQ(persistent_ids, load_result->incoming_messages); ASSERT_TRUE(load_result->outgoing_messages.empty()); gcm_store->RemoveIncomingMessages( persistent_ids, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); load_result->incoming_messages.clear(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_TRUE(load_result->incoming_messages.empty()); ASSERT_TRUE(load_result->outgoing_messages.empty()); } // Verify saving some outgoing messages, reopening the directory, and then // removing those outgoing messages. TEST_F(GCMStoreImplTest, OutgoingMessages) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); std::vector persistent_ids; const int kNumPersistentIds = 10; for (int i = 0; i < kNumPersistentIds; ++i) { persistent_ids.push_back(GetNextPersistentId()); mcs_proto::DataMessageStanza message; message.set_from(kAppName + persistent_ids.back()); message.set_category(kCategoryName + persistent_ids.back()); gcm_store->AddOutgoingMessage( persistent_ids.back(), MCSMessage(message), base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); } gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_TRUE(load_result->incoming_messages.empty()); ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size()); for (int i = 0; i < kNumPersistentIds; ++i) { std::string id = persistent_ids[i]; ASSERT_TRUE(load_result->outgoing_messages[id].get()); const mcs_proto::DataMessageStanza* message = reinterpret_cast( load_result->outgoing_messages[id].get()); ASSERT_EQ(message->from(), kAppName + id); ASSERT_EQ(message->category(), kCategoryName + id); } gcm_store->RemoveOutgoingMessages( persistent_ids, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); load_result->outgoing_messages.clear(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_TRUE(load_result->incoming_messages.empty()); ASSERT_TRUE(load_result->outgoing_messages.empty()); } // Verify incoming and outgoing messages don't conflict. TEST_F(GCMStoreImplTest, IncomingAndOutgoingMessages) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); std::vector 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; message.set_from(kAppName + persistent_ids.back()); message.set_category(kCategoryName + persistent_ids.back()); gcm_store->AddOutgoingMessage( persistent_ids.back(), MCSMessage(message), base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); } gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_EQ(persistent_ids, load_result->incoming_messages); ASSERT_EQ(load_result->outgoing_messages.size(), persistent_ids.size()); for (int i = 0; i < kNumPersistentIds; ++i) { std::string id = persistent_ids[i]; ASSERT_TRUE(load_result->outgoing_messages[id].get()); const mcs_proto::DataMessageStanza* message = reinterpret_cast( load_result->outgoing_messages[id].get()); ASSERT_EQ(message->from(), kAppName + id); ASSERT_EQ(message->category(), kCategoryName + id); } 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(); gcm_store = BuildGCMStore().Pass(); load_result->incoming_messages.clear(); load_result->outgoing_messages.clear(); LoadGCMStore(gcm_store.get(), &load_result); ASSERT_TRUE(load_result->incoming_messages.empty()); ASSERT_TRUE(load_result->outgoing_messages.empty()); } // Test that per-app message limits are enforced, persisted across restarts, // and updated as messages are removed. TEST_F(GCMStoreImplTest, PerAppMessageLimits) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); // 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. gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); // 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(); } } TEST_F(GCMStoreImplTest, AccountMapping) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); // Add account mappings. 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 = base::Time::FromInternalValue(1305734521259935LL); account_mapping2.last_message_id = "message_2"; gcm_store->AddAccountMapping( account_mapping1, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store->AddAccountMapping( account_mapping2, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); EXPECT_EQ(2UL, load_result->account_mappings.size()); GCMStore::AccountMappings::iterator iter = load_result->account_mappings.begin(); 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); EXPECT_EQ(account_mapping1.status_change_timestamp, iter->status_change_timestamp); EXPECT_EQ(account_mapping1.last_message_id, iter->last_message_id); ++iter; 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); EXPECT_EQ(account_mapping2.status_change_timestamp, iter->status_change_timestamp); EXPECT_EQ(account_mapping2.last_message_id, iter->last_message_id); gcm_store->RemoveAccountMapping( account_mapping1.account_id, base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); EXPECT_EQ(1UL, load_result->account_mappings.size()); iter = load_result->account_mappings.begin(); 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); EXPECT_EQ(account_mapping2.status_change_timestamp, iter->status_change_timestamp); EXPECT_EQ(account_mapping2.last_message_id, iter->last_message_id); } TEST_F(GCMStoreImplTest, HeartbeatInterval) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); 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(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); 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(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); 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]); } // 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) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); 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(); } } TEST_F(GCMStoreImplTest, ReloadAfterClose) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); gcm_store->Close(); PumpLoop(); LoadGCMStore(gcm_store.get(), &load_result); } TEST_F(GCMStoreImplTest, LastTokenFetchTime) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); 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(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); EXPECT_EQ(last_token_fetch_time, load_result->last_token_fetch_time); // Negative cases, where the value read is gibberish. gcm_store->SetValueForTesting( "last_token_fetch_time", "gibberish", base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); PumpLoop(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); EXPECT_EQ(base::Time(), load_result->last_token_fetch_time); } TEST_F(GCMStoreImplTest, InstanceIDData) { scoped_ptr gcm_store(BuildGCMStore()); scoped_ptr load_result; LoadGCMStore(gcm_store.get(), &load_result); 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(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); 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(); gcm_store = BuildGCMStore().Pass(); LoadGCMStore(gcm_store.get(), &load_result); 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]); } } // namespace } // namespace gcm