// Copyright (c) 2011 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 <set>
#include <string>
#include <vector>
#include "testing/gtest/include/gtest/gtest.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/string16.h"
#include "base/synchronization/waitable_event.h"
#include "base/task.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/autofill_common_test.h"
#include "chrome/browser/sync/abstract_profile_sync_service_test.h"
#include "chrome/browser/sync/engine/model_changing_syncer_command.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/autofill_change_processor.h"
#include "chrome/browser/sync/glue/autofill_data_type_controller.h"
#include "chrome/browser/sync/glue/autofill_model_associator.h"
#include "chrome/browser/sync/glue/autofill_profile_change_processor.h"
#include "chrome/browser/sync/glue/autofill_profile_data_type_controller.h"
#include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
#include "chrome/browser/sync/syncable/autofill_migration.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
#include "chrome/browser/webdata/autofill_change.h"
#include "chrome/browser/webdata/autofill_entry.h"
#include "chrome/browser/webdata/autofill_table.h"
#include "chrome/browser/webdata/web_database.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/test/profile_mock.h"
#include "chrome/test/sync/engine/test_id_factory.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_source.h"
#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
using base::Time;
using base::WaitableEvent;
using browser_sync::AutofillChangeProcessor;
using browser_sync::AutofillDataTypeController;
using browser_sync::AutofillModelAssociator;
using browser_sync::AutofillProfileChangeProcessor;
using browser_sync::AutofillProfileDataTypeController;
using browser_sync::AutofillProfileModelAssociator;
using browser_sync::DataTypeController;
using browser_sync::GROUP_DB;
using browser_sync::kAutofillTag;
using browser_sync::SyncBackendHostForProfileSyncTest;
using browser_sync::UnrecoverableErrorHandler;
using syncable::CREATE_NEW_UPDATE_ITEM;
using syncable::AUTOFILL;
using syncable::BASE_VERSION;
using syncable::CREATE;
using syncable::GET_BY_SERVER_TAG;
using syncable::INVALID;
using syncable::MutableEntry;
using syncable::OriginalEntries;
using syncable::SERVER_PARENT_ID;
using syncable::SERVER_SPECIFICS;
using syncable::SPECIFICS;
using syncable::UNITTEST;
using syncable::WriterTag;
using syncable::WriteTransaction;
using testing::_;
using testing::DoAll;
using testing::DoDefault;
using testing::ElementsAre;
using testing::Eq;
using testing::Invoke;
using testing::Mock;
using testing::Return;
using testing::SaveArg;
using testing::SetArgumentPointee;
namespace syncable {
class Id;
}
class AutofillTableMock : public AutofillTable {
public:
AutofillTableMock() : AutofillTable(NULL, NULL) {}
MOCK_METHOD2(RemoveFormElement,
bool(const string16& name, const string16& value)); // NOLINT
MOCK_METHOD1(GetAllAutofillEntries,
bool(std::vector<AutofillEntry>* entries)); // NOLINT
MOCK_METHOD3(GetAutofillTimestamps,
bool(const string16& name, // NOLINT
const string16& value,
std::vector<base::Time>* timestamps));
MOCK_METHOD1(UpdateAutofillEntries,
bool(const std::vector<AutofillEntry>&)); // NOLINT
MOCK_METHOD1(GetAutofillProfiles,
bool(std::vector<AutofillProfile*>*)); // NOLINT
MOCK_METHOD1(UpdateAutofillProfile,
bool(const AutofillProfile&)); // NOLINT
MOCK_METHOD1(AddAutofillProfile,
bool(const AutofillProfile&)); // NOLINT
MOCK_METHOD1(RemoveAutofillProfile,
bool(const std::string&)); // NOLINT
};
class WebDatabaseFake : public WebDatabase {
public:
explicit WebDatabaseFake(AutofillTable* autofill_table)
: autofill_table_(autofill_table) {}
virtual AutofillTable* GetAutofillTable() {
return autofill_table_;
}
private:
AutofillTable* autofill_table_;
};
class ProfileSyncServiceAutofillTest;
template<class AutofillProfile>
syncable::ModelType GetModelType() {
return syncable::UNSPECIFIED;
}
template<>
syncable::ModelType GetModelType<AutofillEntry>() {
return syncable::AUTOFILL;
}
template<>
syncable::ModelType GetModelType<AutofillProfile>() {
return syncable::AUTOFILL_PROFILE;
}
class WebDataServiceFake : public WebDataService {
public:
explicit WebDataServiceFake(WebDatabase* web_database)
: web_database_(web_database) {}
virtual bool IsDatabaseLoaded() {
return true;
}
virtual WebDatabase* GetDatabase() {
return web_database_;
}
private:
WebDatabase* web_database_;
};
ACTION_P4(MakeAutofillSyncComponents, service, wd, pdm, dtc) {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!BrowserThread::CurrentlyOn(BrowserThread::DB))
return ProfileSyncFactory::SyncComponents(NULL, NULL);
AutofillModelAssociator* model_associator =
new AutofillModelAssociator(service, wd, pdm);
AutofillChangeProcessor* change_processor =
new AutofillChangeProcessor(model_associator, wd, pdm, dtc);
return ProfileSyncFactory::SyncComponents(model_associator,
change_processor);
}
ACTION_P4(MakeAutofillProfileSyncComponents, service, wd, pdm, dtc) {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!BrowserThread::CurrentlyOn(BrowserThread::DB))
return ProfileSyncFactory::SyncComponents(NULL, NULL);
AutofillProfileModelAssociator* model_associator =
new AutofillProfileModelAssociator(service, wd, pdm);
AutofillProfileChangeProcessor* change_processor =
new AutofillProfileChangeProcessor(model_associator, wd, pdm, dtc);
return ProfileSyncFactory::SyncComponents(model_associator,
change_processor);
}
class AbstractAutofillFactory {
public:
virtual AutofillDataTypeController* CreateDataTypeController(
ProfileSyncFactory *factory,
ProfileMock* profile,
ProfileSyncService* service) = 0;
virtual void SetExpectation(ProfileSyncFactoryMock* factory,
ProfileSyncService* service,
WebDatabase* wd,
PersonalDataManager* pdm,
DataTypeController* dtc) = 0;
virtual ~AbstractAutofillFactory() {}
};
class AutofillEntryFactory : public AbstractAutofillFactory {
public:
browser_sync::AutofillDataTypeController* CreateDataTypeController(
ProfileSyncFactory *factory,
ProfileMock* profile,
ProfileSyncService* service) {
return new AutofillDataTypeController(factory,
profile,
service);
}
void SetExpectation(ProfileSyncFactoryMock* factory,
ProfileSyncService* service,
WebDatabase* wd,
PersonalDataManager* pdm,
DataTypeController* dtc) {
EXPECT_CALL(*factory, CreateAutofillSyncComponents(_,_,_,_)).
WillOnce(MakeAutofillSyncComponents(service, wd, pdm, dtc));
}
};
class AutofillProfileFactory : public AbstractAutofillFactory {
public:
browser_sync::AutofillDataTypeController* CreateDataTypeController(
ProfileSyncFactory *factory,
ProfileMock* profile,
ProfileSyncService* service) {
return new AutofillProfileDataTypeController(factory,
profile,
service);
}
void SetExpectation(ProfileSyncFactoryMock* factory,
ProfileSyncService* service,
WebDatabase* wd,
PersonalDataManager* pdm,
DataTypeController* dtc) {
EXPECT_CALL(*factory, CreateAutofillProfileSyncComponents(_,_,_,_)).
WillOnce(MakeAutofillProfileSyncComponents(service, wd, pdm, dtc));
}
};
class PersonalDataManagerMock: public PersonalDataManager {
public:
MOCK_CONST_METHOD0(IsDataLoaded, bool());
MOCK_METHOD0(LoadProfiles, void());
MOCK_METHOD0(LoadCreditCards, void());
MOCK_METHOD0(Refresh, void());
};
template <class T> class AddAutofillTask;
class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
protected:
ProfileSyncServiceAutofillTest() : db_thread_(BrowserThread::DB) {}
AutofillProfileFactory profile_factory_;
AutofillEntryFactory entry_factory_;
AbstractAutofillFactory* GetFactory(syncable::ModelType type) {
if (type == syncable::AUTOFILL) {
return &entry_factory_;
} else if (type == syncable::AUTOFILL_PROFILE) {
return &profile_factory_;
} else {
NOTREACHED();
return NULL;
}
}
virtual void SetUp() {
profile_.CreateRequestContext();
web_database_.reset(new WebDatabaseFake(&autofill_table_));
web_data_service_ = new WebDataServiceFake(web_database_.get());
personal_data_manager_ = new PersonalDataManagerMock();
EXPECT_CALL(*personal_data_manager_, LoadProfiles()).Times(1);
EXPECT_CALL(*personal_data_manager_, LoadCreditCards()).Times(1);
personal_data_manager_->Init(&profile_);
db_thread_.Start();
notification_service_ = new ThreadNotificationService(&db_thread_);
notification_service_->Init();
}
virtual void TearDown() {
service_.reset();
notification_service_->TearDown();
db_thread_.Stop();
{
// The request context gets deleted on the I/O thread. To prevent a leak
// supply one here.
BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
profile_.ResetRequestContext();
}
MessageLoop::current()->RunAllPending();
}
void StartSyncService(Task* task,
bool will_fail_association,
syncable::ModelType type) {
AbstractAutofillFactory* factory = GetFactory(type);
service_.reset(
new TestProfileSyncService(&factory_, &profile_, "test_user", false,
task));
AutofillDataTypeController* data_type_controller =
factory->CreateDataTypeController(&factory_,
&profile_,
service_.get());
SyncBackendHostForProfileSyncTest::
SetDefaultExpectationsForWorkerCreation(&profile_);
factory->SetExpectation(&factory_,
service_.get(),
web_database_.get(),
personal_data_manager_.get(),
data_type_controller);
EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
WillOnce(ReturnNewDataTypeManager());
EXPECT_CALL(profile_, GetWebDataService(_)).
WillOnce(Return(web_data_service_.get()));
EXPECT_CALL(profile_, GetPersonalDataManager()).
WillRepeatedly(Return(personal_data_manager_.get()));
EXPECT_CALL(*personal_data_manager_, IsDataLoaded()).
WillRepeatedly(Return(true));
// We need tokens to get the tests going
token_service_.IssueAuthTokenForTest(
GaiaConstants::kSyncService, "token");
EXPECT_CALL(profile_, GetTokenService()).
WillRepeatedly(Return(&token_service_));
service_->RegisterDataTypeController(data_type_controller);
service_->Initialize();
MessageLoop::current()->Run();
}
bool AddAutofillSyncNode(const AutofillEntry& entry) {
sync_api::WriteTransaction trans(service_->GetUserShare());
sync_api::ReadNode autofill_root(&trans);
if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag))
return false;
sync_api::WriteNode node(&trans);
std::string tag = AutofillModelAssociator::KeyToTag(entry.key().name(),
entry.key().value());
if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag))
return false;
AutofillChangeProcessor::WriteAutofillEntry(entry, &node);
return true;
}
bool AddAutofillSyncNode(const AutofillProfile& profile) {
sync_api::WriteTransaction trans(service_->GetUserShare());
sync_api::ReadNode autofill_root(&trans);
if (!autofill_root.InitByTagLookup(browser_sync::kAutofillProfileTag))
return false;
sync_api::WriteNode node(&trans);
std::string tag = profile.guid();
if (!node.InitUniqueByCreation(syncable::AUTOFILL_PROFILE,
autofill_root, tag))
return false;
AutofillProfileChangeProcessor::WriteAutofillProfile(profile, &node);
return true;
}
bool GetAutofillEntriesFromSyncDB(std::vector<AutofillEntry>* entries,
std::vector<AutofillProfile>* profiles) {
sync_api::ReadTransaction trans(service_->GetUserShare());
sync_api::ReadNode autofill_root(&trans);
if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag))
return false;
int64 child_id = autofill_root.GetFirstChildId();
while (child_id != sync_api::kInvalidId) {
sync_api::ReadNode child_node(&trans);
if (!child_node.InitByIdLookup(child_id))
return false;
const sync_pb::AutofillSpecifics& autofill(
child_node.GetAutofillSpecifics());
if (autofill.has_value()) {
AutofillKey key(UTF8ToUTF16(autofill.name()),
UTF8ToUTF16(autofill.value()));
std::vector<base::Time> timestamps;
int timestamps_count = autofill.usage_timestamp_size();
for (int i = 0; i < timestamps_count; ++i) {
timestamps.push_back(Time::FromInternalValue(
autofill.usage_timestamp(i)));
}
entries->push_back(AutofillEntry(key, timestamps));
} else if (autofill.has_profile()) {
AutofillProfile p;
p.set_guid(autofill.profile().guid());
AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p,
autofill.profile());
profiles->push_back(p);
}
child_id = child_node.GetSuccessorId();
}
return true;
}
bool GetAutofillProfilesFromSyncDBUnderProfileNode(
std::vector<AutofillProfile>* profiles) {
sync_api::ReadTransaction trans(service_->GetUserShare());
sync_api::ReadNode autofill_root(&trans);
if (!autofill_root.InitByTagLookup(browser_sync::kAutofillProfileTag))
return false;
int64 child_id = autofill_root.GetFirstChildId();
while (child_id != sync_api::kInvalidId) {
sync_api::ReadNode child_node(&trans);
if (!child_node.InitByIdLookup(child_id))
return false;
const sync_pb::AutofillProfileSpecifics& autofill(
child_node.GetAutofillProfileSpecifics());
AutofillProfile p;
p.set_guid(autofill.guid());
AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p,
autofill);
profiles->push_back(p);
child_id = child_node.GetSuccessorId();
}
return true;
}
void SetIdleChangeProcessorExpectations() {
EXPECT_CALL(autofill_table_, RemoveFormElement(_, _)).Times(0);
EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _)).Times(0);
EXPECT_CALL(autofill_table_, UpdateAutofillEntries(_)).Times(0);
}
static AutofillEntry MakeAutofillEntry(const char* name,
const char* value,
time_t timestamp0,
time_t timestamp1) {
std::vector<Time> timestamps;
if (timestamp0 > 0)
timestamps.push_back(Time::FromTimeT(timestamp0));
if (timestamp1 > 0)
timestamps.push_back(Time::FromTimeT(timestamp1));
return AutofillEntry(
AutofillKey(ASCIIToUTF16(name), ASCIIToUTF16(value)), timestamps);
}
static AutofillEntry MakeAutofillEntry(const char* name,
const char* value,
time_t timestamp) {
return MakeAutofillEntry(name, value, timestamp, -1);
}
friend class AddAutofillTask<AutofillEntry>;
friend class AddAutofillTask<AutofillProfile>;
friend class FakeServerUpdater;
BrowserThread db_thread_;
scoped_refptr<ThreadNotificationService> notification_service_;
ProfileMock profile_;
AutofillTableMock autofill_table_;
scoped_ptr<WebDatabaseFake> web_database_;
scoped_refptr<WebDataService> web_data_service_;
scoped_refptr<PersonalDataManagerMock> personal_data_manager_;
};
template <class T>
class AddAutofillTask : public Task {
public:
AddAutofillTask(ProfileSyncServiceAutofillTest* test,
const std::vector<T>& entries)
: test_(test), entries_(entries), success_(false) {
}
virtual void Run() {
if (!test_->CreateRoot(GetModelType<T>()))
return;
for (size_t i = 0; i < entries_.size(); ++i) {
if (!test_->AddAutofillSyncNode(entries_[i]))
return;
}
success_ = true;
}
bool success() { return success_; }
private:
ProfileSyncServiceAutofillTest* test_;
const std::vector<T>& entries_;
bool success_;
};
// Overload write transaction to use custom NotifyTransactionComplete
static const bool kLoggingInfo = true;
class WriteTransactionTest: public WriteTransaction {
public:
WriteTransactionTest(const syncable::ScopedDirLookup& directory,
WriterTag writer, const char* source_file,
int line,
scoped_ptr<WaitableEvent> *wait_for_syncapi)
: WriteTransaction(directory, writer, source_file, line),
wait_for_syncapi_(wait_for_syncapi) { }
virtual void NotifyTransactionComplete(syncable::ModelTypeBitSet types) {
// This is where we differ. Force a thread change here, giving another
// thread a chance to create a WriteTransaction
(*wait_for_syncapi_)->Wait();
WriteTransaction::NotifyTransactionComplete(types);
}
private:
scoped_ptr<WaitableEvent> *wait_for_syncapi_;
};
// Our fake server updater. Needs the RefCountedThreadSafe inheritance so we can
// post tasks with it.
class FakeServerUpdater: public base::RefCountedThreadSafe<FakeServerUpdater> {
public:
FakeServerUpdater(TestProfileSyncService *service,
scoped_ptr<WaitableEvent> *wait_for_start,
scoped_ptr<WaitableEvent> *wait_for_syncapi)
: entry_(ProfileSyncServiceAutofillTest::MakeAutofillEntry("0", "0", 0)),
service_(service),
wait_for_start_(wait_for_start),
wait_for_syncapi_(wait_for_syncapi),
is_finished_(false, false) { }
void Update() {
// This gets called in a modelsafeworker thread.
ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB));
sync_api::UserShare* user_share = service_->GetUserShare();
syncable::DirectoryManager* dir_manager = user_share->dir_manager.get();
syncable::ScopedDirLookup dir(dir_manager, user_share->name);
ASSERT_TRUE(dir.good());
// Create autofill protobuf
std::string tag = AutofillModelAssociator::KeyToTag(entry_.key().name(),
entry_.key().value());
sync_pb::AutofillSpecifics new_autofill;
new_autofill.set_name(UTF16ToUTF8(entry_.key().name()));
new_autofill.set_value(UTF16ToUTF8(entry_.key().value()));
const std::vector<base::Time>& ts(entry_.timestamps());
for (std::vector<base::Time>::const_iterator timestamp = ts.begin();
timestamp != ts.end(); ++timestamp) {
new_autofill.add_usage_timestamp(timestamp->ToInternalValue());
}
sync_pb::EntitySpecifics entity_specifics;
entity_specifics.MutableExtension(sync_pb::autofill)->
CopyFrom(new_autofill);
{
// Tell main thread we've started
(*wait_for_start_)->Signal();
// Create write transaction.
WriteTransactionTest trans(dir, UNITTEST, __FILE__, __LINE__,
wait_for_syncapi_);
// Create actual entry based on autofill protobuf information.
// Simulates effects of SyncerUtil::UpdateLocalDataFromServerData
MutableEntry parent(&trans, GET_BY_SERVER_TAG, kAutofillTag);
MutableEntry item(&trans, CREATE, parent.Get(syncable::ID), tag);
ASSERT_TRUE(item.good());
item.Put(SPECIFICS, entity_specifics);
item.Put(SERVER_SPECIFICS, entity_specifics);
item.Put(BASE_VERSION, 1);
syncable::Id server_item_id = service_->id_factory()->NewServerId();
item.Put(syncable::ID, server_item_id);
syncable::Id new_predecessor;
ASSERT_TRUE(item.PutPredecessor(new_predecessor));
}
VLOG(1) << "FakeServerUpdater finishing.";
is_finished_.Signal();
}
void CreateNewEntry(const AutofillEntry& entry) {
entry_ = entry;
scoped_ptr<Callback0::Type> c(NewCallback((FakeServerUpdater *)this,
&FakeServerUpdater::Update));
std::vector<browser_sync::ModelSafeWorker*> workers;
service_->GetBackendForTest()->GetWorkers(&workers);
ASSERT_FALSE(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
NewRunnableMethod(this, &FakeServerUpdater::Update))) {
NOTREACHED() << "Failed to post task to the db thread.";
return;
}
}
void CreateNewEntryAndWait(const AutofillEntry& entry) {
entry_ = entry;
scoped_ptr<Callback0::Type> c(NewCallback((FakeServerUpdater *)this,
&FakeServerUpdater::Update));
std::vector<browser_sync::ModelSafeWorker*> workers;
service_->GetBackendForTest()->GetWorkers(&workers);
ASSERT_FALSE(BrowserThread::CurrentlyOn(BrowserThread::DB));
is_finished_.Reset();
if (!BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
NewRunnableMethod(this, &FakeServerUpdater::Update))) {
NOTREACHED() << "Failed to post task to the db thread.";
return;
}
is_finished_.Wait();
}
private:
friend class base::RefCountedThreadSafe<FakeServerUpdater>;
~FakeServerUpdater() { }
AutofillEntry entry_;
TestProfileSyncService *service_;
scoped_ptr<WaitableEvent> *wait_for_start_;
scoped_ptr<WaitableEvent> *wait_for_syncapi_;
WaitableEvent is_finished_;
syncable::Id parent_id_;
};
// TODO(skrul): Test abort startup.
// TODO(skrul): Test processing of cloud changes.
// TODO(tim): Add autofill data type controller test, and a case to cover
// waiting for the PersonalDataManager.
TEST_F(ProfileSyncServiceAutofillTest, FailModelAssociation) {
// Don't create the root autofill node so startup fails.
StartSyncService(NULL, true, syncable::AUTOFILL);
EXPECT_TRUE(service_->unrecoverable_error_detected());
}
TEST_F(ProfileSyncServiceAutofillTest, EmptyNativeEmptySync) {
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> sync_entries;
std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
EXPECT_EQ(0U, sync_entries.size());
EXPECT_EQ(0U, sync_profiles.size());
}
TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) {
std::vector<AutofillEntry> entries;
entries.push_back(MakeAutofillEntry("foo", "bar", 1));
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> sync_entries;
std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
ASSERT_EQ(1U, entries.size());
EXPECT_TRUE(entries[0] == sync_entries[0]);
EXPECT_EQ(0U, sync_profiles.size());
}
TEST_F(ProfileSyncServiceAutofillTest, HasProfileEmptySync) {
std::vector<AutofillProfile*> profiles;
std::vector<AutofillProfile> expected_profiles;
// Owned by GetAutofillProfiles caller.
AutofillProfile* profile0 = new AutofillProfile;
autofill_test::SetProfileInfoWithGuid(profile0,
"54B3F9AA-335E-4F71-A27D-719C41564230", "Billing",
"Mitchell", "Morrison",
"johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
"91601", "US", "12345678910", "01987654321");
profiles.push_back(profile0);
expected_profiles.push_back(*profile0);
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(profiles), Return(true)));
EXPECT_CALL(*personal_data_manager_, Refresh());
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL_PROFILE);
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(&sync_profiles));
EXPECT_EQ(1U, sync_profiles.size());
EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[0]));
}
TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) {
// There is buggy autofill code that allows duplicate name/value
// pairs to exist in the database with separate pair_ids.
std::vector<AutofillEntry> entries;
entries.push_back(MakeAutofillEntry("foo", "bar", 1));
entries.push_back(MakeAutofillEntry("dup", "", 2));
entries.push_back(MakeAutofillEntry("dup", "", 3));
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> sync_entries;
std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
EXPECT_EQ(2U, sync_entries.size());
}
TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) {
AutofillEntry native_entry(MakeAutofillEntry("native", "entry", 1));
AutofillEntry sync_entry(MakeAutofillEntry("sync", "entry", 2));
std::vector<AutofillEntry> native_entries;
native_entries.push_back(native_entry);
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
std::vector<AutofillEntry> sync_entries;
sync_entries.push_back(sync_entry);
AddAutofillTask<AutofillEntry> task(this, sync_entries);
EXPECT_CALL(autofill_table_, UpdateAutofillEntries(ElementsAre(sync_entry))).
WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::set<AutofillEntry> expected_entries;
expected_entries.insert(native_entry);
expected_entries.insert(sync_entry);
std::vector<AutofillEntry> new_sync_entries;
std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
std::set<AutofillEntry> new_sync_entries_set(new_sync_entries.begin(),
new_sync_entries.end());
EXPECT_TRUE(expected_entries == new_sync_entries_set);
}
TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) {
AutofillEntry native_entry(MakeAutofillEntry("merge", "entry", 1));
AutofillEntry sync_entry(MakeAutofillEntry("merge", "entry", 2));
AutofillEntry merged_entry(MakeAutofillEntry("merge", "entry", 1, 2));
std::vector<AutofillEntry> native_entries;
native_entries.push_back(native_entry);
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
std::vector<AutofillEntry> sync_entries;
sync_entries.push_back(sync_entry);
AddAutofillTask<AutofillEntry> task(this, sync_entries);
EXPECT_CALL(autofill_table_,
UpdateAutofillEntries(ElementsAre(merged_entry))).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> new_sync_entries;
std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_entries.size());
EXPECT_TRUE(merged_entry == new_sync_entries[0]);
}
TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) {
AutofillProfile sync_profile;
autofill_test::SetProfileInfoWithGuid(&sync_profile,
"23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
"Mitchell", "Morrison",
"johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
"91601", "US", "12345678910", "01987654321");
AutofillProfile* native_profile = new AutofillProfile;
autofill_test::SetProfileInfoWithGuid(native_profile,
"23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", "Alicia", "Saenz",
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
"32801", "US", "19482937549", "13502849239");
std::vector<AutofillProfile*> native_profiles;
native_profiles.push_back(native_profile);
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
std::vector<AutofillProfile> sync_profiles;
sync_profiles.push_back(sync_profile);
AddAutofillTask<AutofillProfile> task(this, sync_profiles);
EXPECT_CALL(autofill_table_, UpdateAutofillProfile(_)).
WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_profiles.size());
EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0]));
}
TEST_F(ProfileSyncServiceAutofillTest, MergeProfileWithDifferentGuid) {
AutofillProfile sync_profile;
autofill_test::SetProfileInfoWithGuid(&sync_profile,
"23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
"Mitchell", "Morrison",
"johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
"91601", "US", "12345678910", "01987654321");
std::string native_guid = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
AutofillProfile* native_profile = new AutofillProfile;
autofill_test::SetProfileInfoWithGuid(native_profile,
native_guid.c_str(), "Billing",
"Mitchell", "Morrison",
"johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
"91601", "US", "12345678910", "01987654321");
std::vector<AutofillProfile*> native_profiles;
native_profiles.push_back(native_profile);
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
std::vector<AutofillProfile> sync_profiles;
sync_profiles.push_back(sync_profile);
AddAutofillTask<AutofillProfile> task(this, sync_profiles);
EXPECT_CALL(autofill_table_, AddAutofillProfile(_)).
WillOnce(Return(true));
EXPECT_CALL(autofill_table_, RemoveAutofillProfile(native_guid)).
WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_profiles.size());
EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0]));
EXPECT_EQ(sync_profile.guid(), new_sync_profiles[0].guid());
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) {
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
AutofillEntry added_entry(MakeAutofillEntry("added", "entry", 1));
std::vector<base::Time> timestamps(added_entry.timestamps());
EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _)).
WillOnce(DoAll(SetArgumentPointee<2>(timestamps), Return(true)));
AutofillChangeList changes;
changes.push_back(AutofillChange(AutofillChange::ADD, added_entry.key()));
scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED,
Source<WebDataService>(web_data_service_.get()),
Details<AutofillChangeList>(&changes));
std::vector<AutofillEntry> new_sync_entries;
std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_entries.size());
EXPECT_TRUE(added_entry == new_sync_entries[0]);
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) {
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL_PROFILE);
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
AutofillProfile added_profile;
autofill_test::SetProfileInfoWithGuid(&added_profile,
"D6ADA912-D374-4C0A-917D-F5C8EBE43011", "Josephine", "Alicia", "Saenz",
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
"32801", "US", "19482937549", "13502849239");
AutofillProfileChange change(AutofillProfileChange::ADD,
added_profile.guid(), &added_profile);
scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED,
Source<WebDataService>(web_data_service_.get()),
Details<AutofillProfileChange>(&change));
std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_profiles.size());
EXPECT_EQ(0, added_profile.Compare(new_sync_profiles[0]));
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) {
AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1));
std::vector<AutofillEntry> original_entries;
original_entries.push_back(original_entry);
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
CreateRootTask task(this, syncable::AUTOFILL);
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
AutofillEntry updated_entry(MakeAutofillEntry("my", "entry", 1, 2));
std::vector<base::Time> timestamps(updated_entry.timestamps());
EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _)).
WillOnce(DoAll(SetArgumentPointee<2>(timestamps), Return(true)));
AutofillChangeList changes;
changes.push_back(AutofillChange(AutofillChange::UPDATE,
updated_entry.key()));
scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED,
Source<WebDataService>(web_data_service_.get()),
Details<AutofillChangeList>(&changes));
std::vector<AutofillEntry> new_sync_entries;
std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_entries.size());
EXPECT_TRUE(updated_entry == new_sync_entries[0]);
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) {
AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1));
std::vector<AutofillEntry> original_entries;
original_entries.push_back(original_entry);
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
CreateRootTask task(this, syncable::AUTOFILL);
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
AutofillChangeList changes;
changes.push_back(AutofillChange(AutofillChange::REMOVE,
original_entry.key()));
scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED,
Source<WebDataService>(web_data_service_.get()),
Details<AutofillChangeList>(&changes));
std::vector<AutofillEntry> new_sync_entries;
std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
ASSERT_EQ(0U, new_sync_entries.size());
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) {
AutofillProfile sync_profile;
autofill_test::SetProfileInfoWithGuid(&sync_profile,
"3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz",
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
"32801", "US", "19482937549", "13502849239");
AutofillProfile* native_profile = new AutofillProfile;
autofill_test::SetProfileInfoWithGuid(native_profile,
"3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz",
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
"32801", "US", "19482937549", "13502849239");
std::vector<AutofillProfile*> native_profiles;
native_profiles.push_back(native_profile);
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
std::vector<AutofillProfile> sync_profiles;
sync_profiles.push_back(sync_profile);
AddAutofillTask<AutofillProfile> task(this, sync_profiles);
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
AutofillProfileChange change(AutofillProfileChange::REMOVE,
sync_profile.guid(), NULL);
scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED,
Source<WebDataService>(web_data_service_.get()),
Details<AutofillProfileChange>(&change));
std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
&new_sync_profiles));
ASSERT_EQ(0U, new_sync_profiles.size());
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeError) {
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
CreateRootTask task(this, syncable::AUTOFILL);
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
// Inject an evil entry into the sync db to conflict with the same
// entry added by the user.
AutofillEntry evil_entry(MakeAutofillEntry("evil", "entry", 1));
ASSERT_TRUE(AddAutofillSyncNode(evil_entry));
AutofillChangeList changes;
changes.push_back(AutofillChange(AutofillChange::ADD,
evil_entry.key()));
scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED,
Source<WebDataService>(web_data_service_.get()),
Details<AutofillChangeList>(&changes));
// Wait for the PPS to shut everything down and signal us.
ProfileSyncServiceObserverMock observer;
service_->AddObserver(&observer);
EXPECT_CALL(observer, OnStateChanged()).WillOnce(QuitUIMessageLoop());
MessageLoop::current()->Run();
EXPECT_TRUE(service_->unrecoverable_error_detected());
// Ensure future autofill notifications don't crash.
notifier->Notify(NotificationType::AUTOFILL_ENTRIES_CHANGED,
Source<WebDataService>(web_data_service_.get()),
Details<AutofillChangeList>(&changes));
}
// Crashy, http://crbug.com/57884
TEST_F(ProfileSyncServiceAutofillTest, DISABLED_ServerChangeRace) {
EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true));
EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(autofill_table_, UpdateAutofillEntries(_)).
WillRepeatedly(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh()).Times(3);
CreateRootTask task(this, syncable::AUTOFILL);
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
// (true, false) means we have to reset after |Signal|, init to unsignaled.
scoped_ptr<WaitableEvent> wait_for_start(new WaitableEvent(true, false));
scoped_ptr<WaitableEvent> wait_for_syncapi(new WaitableEvent(true, false));
scoped_refptr<FakeServerUpdater> updater(new FakeServerUpdater(
service_.get(), &wait_for_start, &wait_for_syncapi));
// This server side update will stall waiting for CommitWaiter.
updater->CreateNewEntry(MakeAutofillEntry("server", "entry", 1));
wait_for_start->Wait();
AutofillEntry syncapi_entry(MakeAutofillEntry("syncapi", "entry", 2));
ASSERT_TRUE(AddAutofillSyncNode(syncapi_entry));
VLOG(1) << "Syncapi update finished.";
// If we reach here, it means syncapi succeeded and we didn't deadlock. Yay!
// Signal FakeServerUpdater that it can complete.
wait_for_syncapi->Signal();
// Make another entry to ensure nothing broke afterwards and wait for finish
// to clean up.
updater->CreateNewEntryAndWait(MakeAutofillEntry("server2", "entry2", 3));
std::vector<AutofillEntry> sync_entries;
std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
EXPECT_EQ(3U, sync_entries.size());
EXPECT_EQ(0U, sync_profiles.size());
for (size_t i = 0; i < sync_entries.size(); i++) {
VLOG(1) << "Entry " << i << ": " << sync_entries[i].key().name()
<< ", " << sync_entries[i].key().value();
}
}