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
30using autofill::AutofillWebDataService;
31using autofill::AutofillWebDataBackend;
32
33namespace browser_sync {
34
35namespace {
36
37using content::BrowserThread;
38using testing::_;
39using testing::Return;
40
41class 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.
55class 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
111class MockWebDataServiceWrapperSyncable : public MockWebDataServiceWrapper {
112 public:
113  static KeyedService* Build(content::BrowserContext* profile) {
114    return new MockWebDataServiceWrapperSyncable();
115  }
116
117  MockWebDataServiceWrapperSyncable()
118      : MockWebDataServiceWrapper(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
136class SyncAutofillDataTypeControllerTest : public testing::Test {
137 public:
138  SyncAutofillDataTypeControllerTest()
139      : thread_bundle_(content::TestBrowserThreadBundle::REAL_DB_THREAD),
140        service_(&profile_),
141        last_start_result_(sync_driver::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  }
158
159  // Passed to AutofillDTC::Start().
160  void OnStartFinished(sync_driver::DataTypeController::ConfigureResult result,
161                       const syncer::SyncMergeResult& local_merge_result,
162                       const syncer::SyncMergeResult& syncer_merge_result) {
163    last_start_result_ = result;
164    last_start_error_ = local_merge_result.error();
165  }
166
167  void OnLoadFinished(syncer::ModelType type, syncer::SyncError error) {
168    EXPECT_FALSE(error.IsSet());
169    EXPECT_EQ(type, syncer::AUTOFILL);
170  }
171
172  virtual void TearDown() {
173    autofill_dtc_ = NULL;
174  }
175
176  void BlockForDBThread() {
177    base::RunLoop run_loop;
178    ASSERT_TRUE(BrowserThread::PostTaskAndReply(BrowserThread::DB, FROM_HERE,
179        base::Bind(&base::DoNothing), run_loop.QuitClosure()));
180    run_loop.Run();
181  }
182
183 protected:
184  content::TestBrowserThreadBundle thread_bundle_;
185  ProfileSyncComponentsFactoryMock profile_sync_factory_;
186  TestingProfile profile_;
187  ProfileSyncServiceMock service_;
188  scoped_refptr<AutofillDataTypeController> autofill_dtc_;
189
190  // Stores arguments of most recent call of OnStartFinished().
191  sync_driver::DataTypeController::ConfigureResult last_start_result_;
192  syncer::SyncError last_start_error_;
193  base::WeakPtrFactory<SyncAutofillDataTypeControllerTest> weak_ptr_factory_;
194};
195
196// Load the WDS's database, then start the Autofill DTC.  It should
197// immediately try to start association and fail (due to missing DB
198// thread).
199TEST_F(SyncAutofillDataTypeControllerTest, StartWDSReady) {
200  FakeWebDataService* web_db =
201      static_cast<FakeWebDataService*>(
202          WebDataServiceFactory::GetAutofillWebDataForProfile(
203              &profile_, Profile::EXPLICIT_ACCESS).get());
204  web_db->LoadDatabase();
205  autofill_dtc_->LoadModels(
206    base::Bind(&SyncAutofillDataTypeControllerTest::OnLoadFinished,
207               weak_ptr_factory_.GetWeakPtr()));
208
209  autofill_dtc_->StartAssociating(
210      base::Bind(&SyncAutofillDataTypeControllerTest::OnStartFinished,
211                 weak_ptr_factory_.GetWeakPtr()));
212  BlockForDBThread();
213
214  EXPECT_EQ(sync_driver::DataTypeController::ASSOCIATION_FAILED,
215            last_start_result_);
216  EXPECT_TRUE(last_start_error_.IsSet());
217  EXPECT_EQ(sync_driver::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).
224TEST_F(SyncAutofillDataTypeControllerTest, StartWDSNotReady) {
225  autofill_dtc_->LoadModels(
226      base::Bind(&SyncAutofillDataTypeControllerTest::OnLoadFinished,
227                 weak_ptr_factory_.GetWeakPtr()));
228
229  EXPECT_EQ(sync_driver::DataTypeController::OK, last_start_result_);
230  EXPECT_FALSE(last_start_error_.IsSet());
231  EXPECT_EQ(sync_driver::DataTypeController::MODEL_STARTING,
232            autofill_dtc_->state());
233
234  FakeWebDataService* web_db = 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(sync_driver::DataTypeController::ASSOCIATION_FAILED,
245            last_start_result_);
246  EXPECT_TRUE(last_start_error_.IsSet());
247
248  EXPECT_EQ(sync_driver::DataTypeController::DISABLED, autofill_dtc_->state());
249}
250
251}  // namespace
252
253}  // namespace browser_sync
254