// Copyright (c) 2010 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 #include #include #include "testing/gtest/include/gtest/gtest.h" #include "base/ref_counted.h" #include "base/string16.h" #include "base/task.h" #include "base/time.h" #include "base/utf_string_conversions.h" #include "base/waitable_event.h" #include "chrome/browser/autofill/autofill_common_unittest.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/sync_backend_host_mock.h" #include "chrome/browser/sync/profile_sync_factory.h" #include "chrome/browser/sync/profile_sync_factory_mock.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/directory_manager.h" #include "chrome/browser/webdata/autofill_change.h" #include "chrome/browser/webdata/autofill_entry.h" #include "chrome/browser/webdata/web_database.h" #include "chrome/common/notification_type.h" #include "chrome/test/sync/engine/test_id_factory.h" #include "chrome/test/profile_mock.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::SyncBackendHostMock; using browser_sync::TestIdFactory; using browser_sync::UnrecoverableErrorHandler; using sync_api::SyncManager; using sync_api::UserShare; using syncable::BASE_VERSION; using syncable::CREATE; using syncable::DirectoryManager; using syncable::ID; using syncable::IS_DEL; using syncable::IS_DIR; using syncable::IS_UNAPPLIED_UPDATE; using syncable::IS_UNSYNCED; using syncable::MutableEntry; using syncable::SERVER_IS_DIR; using syncable::SERVER_VERSION; using syncable::SPECIFICS; using syncable::ScopedDirLookup; using syncable::UNIQUE_SERVER_TAG; using syncable::UNITTEST; using syncable::WriteTransaction; using testing::_; using testing::DoAll; using testing::DoDefault; using testing::ElementsAre; using testing::Eq; using testing::Invoke; using testing::Return; using testing::SaveArg; using testing::SetArgumentPointee; class WebDatabaseMock : public WebDatabase { public: MOCK_METHOD2(RemoveFormElement, bool(const string16& name, const string16& value)); // NOLINT MOCK_METHOD1(GetAllAutofillEntries, bool(std::vector* entries)); // NOLINT MOCK_METHOD3(GetAutofillTimestamps, bool(const string16& name, // NOLINT const string16& value, std::vector* timestamps)); MOCK_METHOD1(UpdateAutofillEntries, bool(const std::vector&)); // NOLINT MOCK_METHOD1(GetAutoFillProfiles, bool(std::vector*)); // NOLINT MOCK_METHOD1(UpdateAutoFillProfile, bool(const AutoFillProfile&)); // NOLINT MOCK_METHOD1(AddAutoFillProfile, bool(const AutoFillProfile&)); // NOLINT MOCK_METHOD1(RemoveAutoFillProfile, bool(int)); // NOLINT }; class WebDataServiceFake : public WebDataService { public: virtual bool IsDatabaseLoaded() { return true; } // Note that we inject the WebDatabase through the // ProfileSyncFactory mock. virtual WebDatabase* GetDatabase() { return NULL; } }; class PersonalDataManagerMock: public PersonalDataManager { public: MOCK_CONST_METHOD0(IsDataLoaded, bool()); MOCK_METHOD0(LoadProfiles, void()); MOCK_METHOD0(LoadCreditCards, void()); MOCK_METHOD0(Refresh, void()); }; ACTION_P4(MakeAutofillSyncComponents, service, wd, pdm, dtc) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); AutofillModelAssociator* model_associator = new AutofillModelAssociator(service, wd, pdm, dtc); AutofillChangeProcessor* change_processor = new AutofillChangeProcessor(model_associator, wd, pdm, dtc); return ProfileSyncFactory::SyncComponents(model_associator, change_processor); } class ProfileSyncServiceAutofillTest : public testing::Test { protected: ProfileSyncServiceAutofillTest() : ui_thread_(ChromeThread::UI, &message_loop_), db_thread_(ChromeThread::DB) { } virtual void SetUp() { web_data_service_ = new WebDataServiceFake(); 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(); MessageLoop::current()->RunAllPending(); } void StartSyncService(Task* task) { if (!service_.get()) { service_.reset( new TestingProfileSyncService(&factory_, &profile_, false)); service_->AddObserver(&observer_); AutofillDataTypeController* data_type_controller = new AutofillDataTypeController(&factory_, &profile_, service_.get()); EXPECT_CALL(factory_, CreateAutofillSyncComponents(_, _, _, _)). WillOnce(MakeAutofillSyncComponents(service_.get(), &web_database_, &personal_data_manager_, data_type_controller)); EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). WillOnce(MakeDataTypeManager(&backend_)); EXPECT_CALL(profile_, GetWebDataService(_)). WillOnce(Return(web_data_service_.get())); EXPECT_CALL(profile_, GetPersonalDataManager()). WillRepeatedly(Return(&personal_data_manager_)); EXPECT_CALL(personal_data_manager_, IsDataLoaded()). WillRepeatedly(Return(true)); // State changes once for the backend init and once for startup done. EXPECT_CALL(observer_, OnStateChanged()). WillOnce(InvokeTask(task)). WillOnce(QuitUIMessageLoop()); service_->RegisterDataTypeController(data_type_controller); service_->Initialize(); MessageLoop::current()->Run(); } } void CreateAutofillRoot() { UserShare* user_share = service_->backend()->GetUserShareHandle(); DirectoryManager* dir_manager = user_share->dir_manager.get(); ScopedDirLookup dir(dir_manager, user_share->authenticated_name); ASSERT_TRUE(dir.good()); WriteTransaction wtrans(dir, UNITTEST, __FILE__, __LINE__); MutableEntry node(&wtrans, CREATE, wtrans.root_id(), browser_sync::kAutofillTag); node.Put(UNIQUE_SERVER_TAG, browser_sync::kAutofillTag); node.Put(IS_DIR, true); node.Put(SERVER_IS_DIR, false); node.Put(IS_UNSYNCED, false); node.Put(IS_UNAPPLIED_UPDATE, false); node.Put(SERVER_VERSION, 20); node.Put(BASE_VERSION, 20); node.Put(IS_DEL, false); node.Put(ID, ids_.MakeServer(browser_sync::kAutofillTag)); sync_pb::EntitySpecifics specifics; specifics.MutableExtension(sync_pb::autofill); node.Put(SPECIFICS, specifics); } void AddAutofillSyncNode(const AutofillEntry& entry) { sync_api::WriteTransaction trans( service_->backend()->GetUserShareHandle()); sync_api::ReadNode autofill_root(&trans); ASSERT_TRUE(autofill_root.InitByTagLookup(browser_sync::kAutofillTag)); sync_api::WriteNode node(&trans); std::string tag = AutofillModelAssociator::KeyToTag(entry.key().name(), entry.key().value()); ASSERT_TRUE(node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)); AutofillChangeProcessor::WriteAutofillEntry(entry, &node); } void AddAutofillProfileSyncNode(const AutoFillProfile& profile) { sync_api::WriteTransaction trans( service_->backend()->GetUserShareHandle()); sync_api::ReadNode autofill_root(&trans); ASSERT_TRUE(autofill_root.InitByTagLookup(browser_sync::kAutofillTag)); sync_api::WriteNode node(&trans); std::string tag = AutofillModelAssociator::ProfileLabelToTag( profile.Label()); ASSERT_TRUE(node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)); AutofillChangeProcessor::WriteAutofillProfile(profile, &node); sync_pb::AutofillSpecifics s(node.GetAutofillSpecifics()); s.mutable_profile()->set_label(UTF16ToUTF8(profile.Label())); node.SetAutofillSpecifics(s); } void GetAutofillEntriesFromSyncDB(std::vector* entries, std::vector* profiles) { sync_api::ReadTransaction trans(service_->backend()->GetUserShareHandle()); sync_api::ReadNode autofill_root(&trans); ASSERT_TRUE(autofill_root.InitByTagLookup(browser_sync::kAutofillTag)); int64 child_id = autofill_root.GetFirstChildId(); while (child_id != sync_api::kInvalidId) { sync_api::ReadNode child_node(&trans); ASSERT_TRUE(child_node.InitByIdLookup(child_id)); const sync_pb::AutofillSpecifics& autofill( child_node.GetAutofillSpecifics()); if (autofill.has_value()) { AutofillKey key(UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value())); std::vector 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(UTF8ToUTF16(autofill.profile().label()), 0); AutofillModelAssociator::OverwriteProfileWithServerData(&p, autofill.profile()); profiles->push_back(p); } child_id = child_node.GetSuccessorId(); } } void SetIdleChangeProcessorExpectations() { EXPECT_CALL(web_database_, RemoveFormElement(_, _)).Times(0); EXPECT_CALL(web_database_, GetAutofillTimestamps(_, _, _)).Times(0); EXPECT_CALL(web_database_, UpdateAutofillEntries(_)).Times(0); } static AutofillEntry MakeAutofillEntry(const char* name, const char* value, time_t timestamp0, time_t timestamp1) { std::vector