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 "chrome/browser/sync/glue/bookmark_data_type_controller.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/callback.h" 10#include "base/memory/scoped_ptr.h" 11#include "base/prefs/pref_service.h" 12#include "base/run_loop.h" 13#include "chrome/browser/bookmarks/bookmark_model_factory.h" 14#include "chrome/browser/bookmarks/chrome_bookmark_client.h" 15#include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h" 16#include "chrome/browser/chrome_notification_types.h" 17#include "chrome/browser/history/history_service.h" 18#include "chrome/browser/history/history_service_factory.h" 19#include "chrome/browser/profiles/profile.h" 20#include "chrome/browser/sync/profile_sync_components_factory_mock.h" 21#include "chrome/browser/sync/profile_sync_service_mock.h" 22#include "chrome/common/pref_names.h" 23#include "chrome/test/base/profile_mock.h" 24#include "components/bookmarks/browser/bookmark_model.h" 25#include "components/bookmarks/test/bookmark_test_helpers.h" 26#include "components/keyed_service/content/refcounted_browser_context_keyed_service.h" 27#include "components/sync_driver/change_processor_mock.h" 28#include "components/sync_driver/data_type_controller_mock.h" 29#include "components/sync_driver/model_associator_mock.h" 30#include "content/public/browser/browser_thread.h" 31#include "content/public/browser/notification_service.h" 32#include "content/public/test/test_browser_thread_bundle.h" 33#include "sync/api/sync_error.h" 34#include "testing/gmock/include/gmock/gmock.h" 35#include "testing/gtest/include/gtest/gtest.h" 36 37using browser_sync::BookmarkDataTypeController; 38using sync_driver::ChangeProcessorMock; 39using sync_driver::DataTypeController; 40using sync_driver::ModelAssociatorMock; 41using sync_driver::ModelLoadCallbackMock; 42using sync_driver::StartCallbackMock; 43using testing::_; 44using testing::DoAll; 45using testing::InvokeWithoutArgs; 46using testing::Return; 47using testing::SetArgumentPointee; 48 49namespace { 50 51class HistoryMock : public HistoryService { 52 public: 53 explicit HistoryMock(history::HistoryClient* client, Profile* profile) 54 : HistoryService(client, profile) {} 55 MOCK_METHOD0(BackendLoaded, bool(void)); 56 57 protected: 58 virtual ~HistoryMock() {} 59}; 60 61KeyedService* BuildChromeBookmarkClient(content::BrowserContext* context) { 62 return new ChromeBookmarkClient(static_cast<Profile*>(context)); 63} 64 65KeyedService* BuildBookmarkModelWithoutLoading( 66 content::BrowserContext* context) { 67 Profile* profile = static_cast<Profile*>(context); 68 ChromeBookmarkClient* bookmark_client = 69 ChromeBookmarkClientFactory::GetForProfile(profile); 70 BookmarkModel* bookmark_model = new BookmarkModel(bookmark_client); 71 bookmark_client->Init(bookmark_model); 72 return bookmark_model; 73} 74 75KeyedService* BuildBookmarkModel(content::BrowserContext* context) { 76 BookmarkModel* bookmark_model = static_cast<BookmarkModel*>( 77 BuildBookmarkModelWithoutLoading(context)); 78 Profile* profile = static_cast<Profile*>(context); 79 bookmark_model->Load(profile->GetPrefs(), 80 profile->GetPrefs()->GetString(prefs::kAcceptLanguages), 81 profile->GetPath(), 82 profile->GetIOTaskRunner(), 83 content::BrowserThread::GetMessageLoopProxyForThread( 84 content::BrowserThread::UI)); 85 return bookmark_model; 86} 87 88KeyedService* BuildHistoryService(content::BrowserContext* profile) { 89 return new HistoryMock(NULL, static_cast<Profile*>(profile)); 90} 91 92} // namespace 93 94class SyncBookmarkDataTypeControllerTest : public testing::Test { 95 public: 96 SyncBookmarkDataTypeControllerTest() 97 : thread_bundle_(content::TestBrowserThreadBundle::DEFAULT), 98 service_(&profile_) {} 99 100 virtual void SetUp() { 101 model_associator_ = new ModelAssociatorMock(); 102 change_processor_ = new ChangeProcessorMock(); 103 history_service_ = static_cast<HistoryMock*>( 104 HistoryServiceFactory::GetInstance()->SetTestingFactoryAndUse( 105 &profile_, BuildHistoryService)); 106 profile_sync_factory_.reset( 107 new ProfileSyncComponentsFactoryMock(model_associator_, 108 change_processor_)); 109 bookmark_dtc_ = new BookmarkDataTypeController(profile_sync_factory_.get(), 110 &profile_, 111 &service_); 112 } 113 114 protected: 115 enum BookmarkLoadPolicy { 116 DONT_LOAD_MODEL, 117 LOAD_MODEL, 118 }; 119 120 void CreateBookmarkModel(BookmarkLoadPolicy bookmark_load_policy) { 121 ChromeBookmarkClientFactory::GetInstance()->SetTestingFactory( 122 &profile_, BuildChromeBookmarkClient); 123 if (bookmark_load_policy == LOAD_MODEL) { 124 bookmark_model_ = static_cast<BookmarkModel*>( 125 BookmarkModelFactory::GetInstance()->SetTestingFactoryAndUse( 126 &profile_, BuildBookmarkModel)); 127 test::WaitForBookmarkModelToLoad(bookmark_model_); 128 } else { 129 bookmark_model_ = static_cast<BookmarkModel*>( 130 BookmarkModelFactory::GetInstance()->SetTestingFactoryAndUse( 131 &profile_, BuildBookmarkModelWithoutLoading)); 132 } 133 } 134 135 void SetStartExpectations() { 136 EXPECT_CALL(*history_service_, 137 BackendLoaded()).WillRepeatedly(Return(true)); 138 EXPECT_CALL(model_load_callback_, Run(_, _)); 139 } 140 141 void SetAssociateExpectations() { 142 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 143 WillRepeatedly(Return(true)); 144 EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _)); 145 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 146 WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true))); 147 EXPECT_CALL(*model_associator_, AssociateModels(_, _)). 148 WillRepeatedly(Return(syncer::SyncError())); 149 } 150 151 void SetStopExpectations() { 152 EXPECT_CALL(service_, DeactivateDataType(_)); 153 EXPECT_CALL(*model_associator_, DisassociateModels()). 154 WillOnce(Return(syncer::SyncError())); 155 } 156 157 void Start() { 158 bookmark_dtc_->LoadModels( 159 base::Bind(&ModelLoadCallbackMock::Run, 160 base::Unretained(&model_load_callback_))); 161 bookmark_dtc_->StartAssociating( 162 base::Bind(&StartCallbackMock::Run, 163 base::Unretained(&start_callback_))); 164 } 165 166 content::TestBrowserThreadBundle thread_bundle_; 167 scoped_refptr<BookmarkDataTypeController> bookmark_dtc_; 168 scoped_ptr<ProfileSyncComponentsFactoryMock> profile_sync_factory_; 169 ProfileMock profile_; 170 BookmarkModel* bookmark_model_; 171 HistoryMock* history_service_; 172 ProfileSyncServiceMock service_; 173 ModelAssociatorMock* model_associator_; 174 ChangeProcessorMock* change_processor_; 175 StartCallbackMock start_callback_; 176 ModelLoadCallbackMock model_load_callback_; 177}; 178 179TEST_F(SyncBookmarkDataTypeControllerTest, StartDependentsReady) { 180 CreateBookmarkModel(LOAD_MODEL); 181 SetStartExpectations(); 182 SetAssociateExpectations(); 183 184 EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state()); 185 186 EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _, _)); 187 Start(); 188 EXPECT_EQ(DataTypeController::RUNNING, bookmark_dtc_->state()); 189} 190 191TEST_F(SyncBookmarkDataTypeControllerTest, StartBookmarkModelNotReady) { 192 CreateBookmarkModel(DONT_LOAD_MODEL); 193 SetStartExpectations(); 194 SetAssociateExpectations(); 195 196 EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _, _)); 197 bookmark_dtc_->LoadModels( 198 base::Bind(&ModelLoadCallbackMock::Run, 199 base::Unretained(&model_load_callback_))); 200 EXPECT_EQ(DataTypeController::MODEL_STARTING, bookmark_dtc_->state()); 201 202 bookmark_model_->Load(profile_.GetPrefs(), 203 profile_.GetPrefs()->GetString(prefs::kAcceptLanguages), 204 profile_.GetPath(), 205 profile_.GetIOTaskRunner(), 206 content::BrowserThread::GetMessageLoopProxyForThread( 207 content::BrowserThread::UI)); 208 test::WaitForBookmarkModelToLoad(bookmark_model_); 209 EXPECT_EQ(DataTypeController::MODEL_LOADED, bookmark_dtc_->state()); 210 211 bookmark_dtc_->StartAssociating( 212 base::Bind(&StartCallbackMock::Run, 213 base::Unretained(&start_callback_))); 214 215 EXPECT_EQ(DataTypeController::RUNNING, bookmark_dtc_->state()); 216} 217 218TEST_F(SyncBookmarkDataTypeControllerTest, StartHistoryServiceNotReady) { 219 CreateBookmarkModel(LOAD_MODEL); 220 SetStartExpectations(); 221 EXPECT_CALL(*history_service_, 222 BackendLoaded()).WillRepeatedly(Return(false)); 223 224 bookmark_dtc_->LoadModels( 225 base::Bind(&ModelLoadCallbackMock::Run, 226 base::Unretained(&model_load_callback_))); 227 228 EXPECT_EQ(DataTypeController::MODEL_STARTING, bookmark_dtc_->state()); 229 testing::Mock::VerifyAndClearExpectations(history_service_); 230 EXPECT_CALL(*history_service_, BackendLoaded()).WillRepeatedly(Return(true)); 231 232 // Send the notification that the history service has finished loading the db. 233 content::NotificationService::current()->Notify( 234 chrome::NOTIFICATION_HISTORY_LOADED, 235 content::Source<Profile>(&profile_), 236 content::NotificationService::NoDetails()); 237 EXPECT_EQ(DataTypeController::MODEL_LOADED, bookmark_dtc_->state()); 238} 239 240TEST_F(SyncBookmarkDataTypeControllerTest, StartFirstRun) { 241 CreateBookmarkModel(LOAD_MODEL); 242 SetStartExpectations(); 243 SetAssociateExpectations(); 244 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 245 WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(true))); 246 EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN, _, _)); 247 Start(); 248} 249 250TEST_F(SyncBookmarkDataTypeControllerTest, StartBusy) { 251 CreateBookmarkModel(LOAD_MODEL); 252 EXPECT_CALL(*history_service_, BackendLoaded()).WillRepeatedly(Return(false)); 253 254 EXPECT_CALL(model_load_callback_, Run(_, _)); 255 bookmark_dtc_->LoadModels( 256 base::Bind(&ModelLoadCallbackMock::Run, 257 base::Unretained(&model_load_callback_))); 258 bookmark_dtc_->LoadModels( 259 base::Bind(&ModelLoadCallbackMock::Run, 260 base::Unretained(&model_load_callback_))); 261} 262 263TEST_F(SyncBookmarkDataTypeControllerTest, StartOk) { 264 CreateBookmarkModel(LOAD_MODEL); 265 SetStartExpectations(); 266 SetAssociateExpectations(); 267 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 268 WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true))); 269 270 EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _, _)); 271 Start(); 272} 273 274TEST_F(SyncBookmarkDataTypeControllerTest, StartAssociationFailed) { 275 CreateBookmarkModel(LOAD_MODEL); 276 SetStartExpectations(); 277 // Set up association to fail. 278 EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _)); 279 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 280 WillRepeatedly(Return(true)); 281 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 282 WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true))); 283 EXPECT_CALL(*model_associator_, AssociateModels(_, _)). 284 WillRepeatedly(Return(syncer::SyncError(FROM_HERE, 285 syncer::SyncError::DATATYPE_ERROR, 286 "error", 287 syncer::BOOKMARKS))); 288 289 EXPECT_CALL(start_callback_, 290 Run(DataTypeController::ASSOCIATION_FAILED, _, _)); 291 Start(); 292 EXPECT_EQ(DataTypeController::DISABLED, bookmark_dtc_->state()); 293} 294 295TEST_F(SyncBookmarkDataTypeControllerTest, 296 StartAssociationTriggersUnrecoverableError) { 297 CreateBookmarkModel(LOAD_MODEL); 298 SetStartExpectations(); 299 // Set up association to fail with an unrecoverable error. 300 EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _)); 301 EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()). 302 WillRepeatedly(Return(true)); 303 EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)). 304 WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false))); 305 EXPECT_CALL(start_callback_, 306 Run(DataTypeController::UNRECOVERABLE_ERROR, _, _)); 307 Start(); 308 EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state()); 309} 310 311TEST_F(SyncBookmarkDataTypeControllerTest, StartAborted) { 312 CreateBookmarkModel(LOAD_MODEL); 313 EXPECT_CALL(*history_service_, BackendLoaded()).WillRepeatedly(Return(false)); 314 315 bookmark_dtc_->LoadModels( 316 base::Bind(&ModelLoadCallbackMock::Run, 317 base::Unretained(&model_load_callback_))); 318 319 bookmark_dtc_->Stop(); 320 EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state()); 321} 322 323TEST_F(SyncBookmarkDataTypeControllerTest, Stop) { 324 CreateBookmarkModel(LOAD_MODEL); 325 SetStartExpectations(); 326 SetAssociateExpectations(); 327 SetStopExpectations(); 328 329 EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state()); 330 331 EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _, _)); 332 Start(); 333 EXPECT_EQ(DataTypeController::RUNNING, bookmark_dtc_->state()); 334 bookmark_dtc_->Stop(); 335 EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state()); 336} 337