1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/bind.h" 6 #include "base/callback.h" 7 #include "base/compiler_specific.h" 8 #include "base/memory/ref_counted.h" 9 #include "base/memory/weak_ptr.h" 10 #include "base/run_loop.h" 11 #include "chrome/browser/chrome_notification_types.h" 12 #include "chrome/browser/sync/glue/autofill_data_type_controller.h" 13 #include "chrome/browser/sync/profile_sync_components_factory_mock.h" 14 #include "chrome/browser/sync/profile_sync_service_factory.h" 15 #include "chrome/browser/sync/profile_sync_service_mock.h" 16 #include "chrome/browser/webdata/autocomplete_syncable_service.h" 17 #include "chrome/browser/webdata/web_data_service_factory.h" 18 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" 19 #include "components/sync_driver/data_type_controller_mock.h" 20 #include "components/webdata/common/web_data_service_test_util.h" 21 #include "content/public/browser/browser_thread.h" 22 #include "content/public/browser/notification_service.h" 23 #include "content/public/browser/notification_source.h" 24 #include "content/public/browser/notification_types.h" 25 #include "content/public/test/test_browser_thread_bundle.h" 26 #include "sync/api/sync_error.h" 27 #include "testing/gmock/include/gmock/gmock.h" 28 #include "testing/gtest/include/gtest/gtest.h" 29 30 using autofill::AutofillWebDataService; 31 using autofill::AutofillWebDataBackend; 32 33 namespace browser_sync { 34 35 namespace { 36 37 using content::BrowserThread; 38 using testing::_; 39 using testing::Return; 40 41 class NoOpAutofillBackend : public AutofillWebDataBackend { 42 public: 43 NoOpAutofillBackend() {} 44 virtual ~NoOpAutofillBackend() {} 45 virtual WebDatabase* GetDatabase() OVERRIDE { return NULL; } 46 virtual void AddObserver( 47 autofill::AutofillWebDataServiceObserverOnDBThread* observer) OVERRIDE {} 48 virtual void RemoveObserver( 49 autofill::AutofillWebDataServiceObserverOnDBThread* observer) OVERRIDE {} 50 virtual void RemoveExpiredFormElements() OVERRIDE {} 51 virtual void NotifyOfMultipleAutofillChanges() OVERRIDE {} 52 }; 53 54 // Fake WebDataService implementation that stubs out the database loading. 55 class FakeWebDataService : public AutofillWebDataService { 56 public: 57 FakeWebDataService() 58 : AutofillWebDataService( 59 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 60 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)), 61 is_database_loaded_(false), 62 db_loaded_callback_(base::Callback<void(void)>()){} 63 64 // Mark the database as loaded and send out the appropriate notification. 65 void LoadDatabase() { 66 StartSyncableService(); 67 is_database_loaded_ = true; 68 69 if (!db_loaded_callback_.is_null()) 70 db_loaded_callback_.Run(); 71 } 72 73 virtual bool IsDatabaseLoaded() OVERRIDE { 74 return is_database_loaded_; 75 } 76 77 virtual void RegisterDBLoadedCallback( 78 const base::Callback<void(void)>& callback) OVERRIDE { 79 db_loaded_callback_ = callback; 80 } 81 82 void StartSyncableService() { 83 // The |autofill_profile_syncable_service_| must be constructed on the DB 84 // thread. 85 base::RunLoop run_loop; 86 BrowserThread::PostTaskAndReply(BrowserThread::DB, FROM_HERE, 87 base::Bind(&FakeWebDataService::CreateSyncableService, 88 base::Unretained(this)), run_loop.QuitClosure()); 89 run_loop.Run(); 90 } 91 92 private: 93 virtual ~FakeWebDataService() { 94 } 95 96 void CreateSyncableService() { 97 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); 98 // These services are deleted in DestroySyncableService(). 99 AutocompleteSyncableService::CreateForWebDataServiceAndBackend( 100 this, 101 &autofill_backend_); 102 } 103 104 bool is_database_loaded_; 105 NoOpAutofillBackend autofill_backend_; 106 base::Callback<void(void)> db_loaded_callback_; 107 108 DISALLOW_COPY_AND_ASSIGN(FakeWebDataService); 109 }; 110 111 class MockWebDataServiceWrapperSyncable : public MockWebDataServiceWrapper { 112 public: 113 static KeyedService* Build(content::BrowserContext* profile) { 114 return new MockWebDataServiceWrapperSyncable(); 115 } 116 117 MockWebDataServiceWrapperSyncable() 118 : MockWebDataServiceWrapper(NULL, new FakeWebDataService(), NULL) { 119 } 120 121 virtual void Shutdown() OVERRIDE { 122 static_cast<FakeWebDataService*>( 123 fake_autofill_web_data_.get())->ShutdownOnUIThread(); 124 // Make sure WebDataService is shutdown properly on DB thread before we 125 // destroy it. 126 base::RunLoop run_loop; 127 ASSERT_TRUE(BrowserThread::PostTaskAndReply(BrowserThread::DB, FROM_HERE, 128 base::Bind(&base::DoNothing), run_loop.QuitClosure())); 129 run_loop.Run(); 130 } 131 132 private: 133 DISALLOW_COPY_AND_ASSIGN(MockWebDataServiceWrapperSyncable); 134 }; 135 136 class SyncAutofillDataTypeControllerTest : public testing::Test { 137 public: 138 SyncAutofillDataTypeControllerTest() 139 : thread_bundle_(content::TestBrowserThreadBundle::REAL_DB_THREAD), 140 service_(&profile_), 141 last_start_result_(DataTypeController::OK), 142 weak_ptr_factory_(this) {} 143 144 virtual ~SyncAutofillDataTypeControllerTest() {} 145 146 virtual void SetUp() { 147 EXPECT_CALL(profile_sync_factory_, 148 GetSyncableServiceForType(_)). 149 WillRepeatedly(Return(base::WeakPtr<syncer::SyncableService>())); 150 151 WebDataServiceFactory::GetInstance()->SetTestingFactory( 152 &profile_, MockWebDataServiceWrapperSyncable::Build); 153 154 autofill_dtc_ = 155 new AutofillDataTypeController( 156 &profile_sync_factory_, &profile_, 157 DataTypeController::DisableTypeCallback()); 158 } 159 160 // Passed to AutofillDTC::Start(). 161 void OnStartFinished(DataTypeController::StartResult result, 162 const syncer::SyncMergeResult& local_merge_result, 163 const syncer::SyncMergeResult& syncer_merge_result) { 164 last_start_result_ = result; 165 last_start_error_ = local_merge_result.error(); 166 } 167 168 void OnLoadFinished(syncer::ModelType type, syncer::SyncError error) { 169 EXPECT_FALSE(error.IsSet()); 170 EXPECT_EQ(type, syncer::AUTOFILL); 171 } 172 173 virtual void TearDown() { 174 autofill_dtc_ = NULL; 175 } 176 177 void BlockForDBThread() { 178 base::RunLoop run_loop; 179 ASSERT_TRUE(BrowserThread::PostTaskAndReply(BrowserThread::DB, FROM_HERE, 180 base::Bind(&base::DoNothing), run_loop.QuitClosure())); 181 run_loop.Run(); 182 } 183 184 protected: 185 content::TestBrowserThreadBundle thread_bundle_; 186 ProfileSyncComponentsFactoryMock profile_sync_factory_; 187 TestingProfile profile_; 188 ProfileSyncServiceMock service_; 189 scoped_refptr<AutofillDataTypeController> autofill_dtc_; 190 191 // Stores arguments of most recent call of OnStartFinished(). 192 DataTypeController::StartResult last_start_result_; 193 syncer::SyncError last_start_error_; 194 base::WeakPtrFactory<SyncAutofillDataTypeControllerTest> weak_ptr_factory_; 195 }; 196 197 // Load the WDS's database, then start the Autofill DTC. It should 198 // immediately try to start association and fail (due to missing DB 199 // thread). 200 TEST_F(SyncAutofillDataTypeControllerTest, StartWDSReady) { 201 FakeWebDataService* web_db = 202 static_cast<FakeWebDataService*>( 203 WebDataServiceFactory::GetAutofillWebDataForProfile( 204 &profile_, Profile::EXPLICIT_ACCESS).get()); 205 web_db->LoadDatabase(); 206 autofill_dtc_->LoadModels( 207 base::Bind(&SyncAutofillDataTypeControllerTest::OnLoadFinished, 208 weak_ptr_factory_.GetWeakPtr())); 209 210 autofill_dtc_->StartAssociating( 211 base::Bind(&SyncAutofillDataTypeControllerTest::OnStartFinished, 212 weak_ptr_factory_.GetWeakPtr())); 213 BlockForDBThread(); 214 215 EXPECT_EQ(DataTypeController::ASSOCIATION_FAILED, last_start_result_); 216 EXPECT_TRUE(last_start_error_.IsSet()); 217 EXPECT_EQ(DataTypeController::DISABLED, autofill_dtc_->state()); 218 } 219 220 // Start the autofill DTC without the WDS's database loaded, then 221 // start the DB. The Autofill DTC should be in the MODEL_STARTING 222 // state until the database in loaded, when it should try to start 223 // association and fail (due to missing DB thread). 224 TEST_F(SyncAutofillDataTypeControllerTest, StartWDSNotReady) { 225 autofill_dtc_->LoadModels( 226 base::Bind(&SyncAutofillDataTypeControllerTest::OnLoadFinished, 227 weak_ptr_factory_.GetWeakPtr())); 228 229 EXPECT_EQ(DataTypeController::OK, last_start_result_); 230 EXPECT_FALSE(last_start_error_.IsSet()); 231 EXPECT_EQ(DataTypeController::MODEL_STARTING, autofill_dtc_->state()); 232 233 FakeWebDataService* web_db = 234 static_cast<FakeWebDataService*>( 235 WebDataServiceFactory::GetAutofillWebDataForProfile( 236 &profile_, Profile::EXPLICIT_ACCESS).get()); 237 web_db->LoadDatabase(); 238 239 autofill_dtc_->StartAssociating( 240 base::Bind(&SyncAutofillDataTypeControllerTest::OnStartFinished, 241 weak_ptr_factory_.GetWeakPtr())); 242 BlockForDBThread(); 243 244 EXPECT_EQ(DataTypeController::ASSOCIATION_FAILED, last_start_result_); 245 EXPECT_TRUE(last_start_error_.IsSet()); 246 247 EXPECT_EQ(DataTypeController::DISABLED, autofill_dtc_->state()); 248 } 249 250 } // namespace 251 252 } // namespace browser_sync 253