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 "testing/gtest/include/gtest/gtest.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/callback.h"
10#include "base/compiler_specific.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop/message_loop.h"
13#include "base/synchronization/waitable_event.h"
14#include "base/test/test_timeouts.h"
15#include "base/tracked_objects.h"
16#include "chrome/browser/sync/glue/change_processor_mock.h"
17#include "chrome/browser/sync/glue/data_type_controller_mock.h"
18#include "chrome/browser/sync/glue/model_associator_mock.h"
19#include "chrome/browser/sync/glue/non_frontend_data_type_controller.h"
20#include "chrome/browser/sync/glue/non_frontend_data_type_controller_mock.h"
21#include "chrome/browser/sync/profile_sync_components_factory_mock.h"
22#include "chrome/browser/sync/profile_sync_service_mock.h"
23#include "chrome/test/base/profile_mock.h"
24#include "content/public/test/test_browser_thread.h"
25#include "sync/internal_api/public/engine/model_safe_worker.h"
26
27using base::WaitableEvent;
28using browser_sync::ChangeProcessorMock;
29using browser_sync::DataTypeController;
30using syncer::GROUP_DB;
31using browser_sync::NonFrontendDataTypeController;
32using browser_sync::NonFrontendDataTypeControllerMock;
33using browser_sync::ModelAssociatorMock;
34using browser_sync::ModelLoadCallbackMock;
35using browser_sync::StartCallbackMock;
36using content::BrowserThread;
37using testing::_;
38using testing::DoAll;
39using testing::InvokeWithoutArgs;
40using testing::Return;
41using testing::SetArgumentPointee;
42using testing::StrictMock;
43
44ACTION_P(WaitOnEvent, event) {
45  event->Wait();
46}
47
48ACTION_P(SignalEvent, event) {
49  event->Signal();
50}
51
52class NonFrontendDataTypeControllerFake : public NonFrontendDataTypeController {
53 public:
54  NonFrontendDataTypeControllerFake(
55      ProfileSyncComponentsFactory* profile_sync_factory,
56      Profile* profile,
57      ProfileSyncService* sync_service,
58      NonFrontendDataTypeControllerMock* mock)
59      : NonFrontendDataTypeController(profile_sync_factory,
60                                      profile,
61                                      sync_service),
62        mock_(mock) {}
63
64  virtual syncer::ModelType type() const OVERRIDE { return syncer::BOOKMARKS; }
65  virtual syncer::ModelSafeGroup model_safe_group() const OVERRIDE {
66    return syncer::GROUP_DB;
67  }
68
69 private:
70  virtual ~NonFrontendDataTypeControllerFake() {}
71
72  virtual ProfileSyncComponentsFactory::SyncComponents
73  CreateSyncComponents() OVERRIDE {
74    return profile_sync_factory()->
75            CreateBookmarkSyncComponents(profile_sync_service(), this);
76  }
77
78  virtual bool PostTaskOnBackendThread(
79      const tracked_objects::Location& from_here,
80      const base::Closure& task) OVERRIDE {
81    return BrowserThread::PostTask(BrowserThread::DB, from_here, task);
82  }
83
84  // We mock the following methods because their default implementations do
85  // nothing, but we still want to make sure they're called appropriately.
86  virtual bool StartModels() OVERRIDE {
87    return mock_->StartModels();
88  }
89  virtual void RecordUnrecoverableError(
90      const tracked_objects::Location& from_here,
91      const std::string& message) OVERRIDE {
92    mock_->RecordUnrecoverableError(from_here, message);
93  }
94  virtual void RecordAssociationTime(base::TimeDelta time) OVERRIDE {
95    mock_->RecordAssociationTime(time);
96  }
97  virtual void RecordStartFailure(
98      DataTypeController::StartResult result) OVERRIDE {
99    mock_->RecordStartFailure(result);
100  }
101  virtual void DisconnectProcessor(
102      browser_sync::ChangeProcessor* processor) OVERRIDE{
103    mock_->DisconnectProcessor(processor);
104  }
105
106 private:
107  NonFrontendDataTypeControllerMock* mock_;
108};
109
110class SyncNonFrontendDataTypeControllerTest : public testing::Test {
111 public:
112  SyncNonFrontendDataTypeControllerTest()
113      : ui_thread_(BrowserThread::UI, &message_loop_),
114        db_thread_(BrowserThread::DB),
115        service_(&profile_),
116        model_associator_(NULL),
117        change_processor_(NULL) {}
118
119  virtual void SetUp() {
120    db_thread_.Start();
121    profile_sync_factory_.reset(
122        new StrictMock<ProfileSyncComponentsFactoryMock>());
123
124    // All of these are refcounted, so don't need to be released.
125    dtc_mock_ = new StrictMock<NonFrontendDataTypeControllerMock>();
126    non_frontend_dtc_ =
127        new NonFrontendDataTypeControllerFake(profile_sync_factory_.get(),
128                                              &profile_,
129                                              &service_,
130                                              dtc_mock_.get());
131  }
132
133  virtual void TearDown() {
134    if (non_frontend_dtc_->state() !=
135        NonFrontendDataTypeController::NOT_RUNNING) {
136      non_frontend_dtc_->Stop();
137    }
138    db_thread_.Stop();
139  }
140
141 protected:
142  void SetStartExpectations() {
143    EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(true));
144    EXPECT_CALL(model_load_callback_, Run(_, _));
145    model_associator_ = new ModelAssociatorMock();
146    change_processor_ = new ChangeProcessorMock();
147    EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _)).
148        WillOnce(Return(ProfileSyncComponentsFactory::SyncComponents(
149            model_associator_, change_processor_)));
150  }
151
152  void SetAssociateExpectations() {
153    EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
154        WillOnce(Return(true));
155    EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
156        WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
157    EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
158        WillOnce(Return(syncer::SyncError()));
159    EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
160  }
161
162  void SetActivateExpectations(DataTypeController::StartResult result) {
163    EXPECT_CALL(service_, ActivateDataType(_, _, _));
164    EXPECT_CALL(start_callback_, Run(result, _, _));
165  }
166
167  void SetStopExpectations() {
168    EXPECT_CALL(*dtc_mock_.get(), DisconnectProcessor(_));
169    EXPECT_CALL(service_, DeactivateDataType(_));
170    EXPECT_CALL(*model_associator_, DisassociateModels()).
171                WillOnce(Return(syncer::SyncError()));
172  }
173
174  void SetStartFailExpectations(DataTypeController::StartResult result) {
175    if (DataTypeController::IsUnrecoverableResult(result))
176      EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, _));
177    if (model_associator_) {
178      EXPECT_CALL(*model_associator_, DisassociateModels()).
179                  WillOnce(Return(syncer::SyncError()));
180    }
181    EXPECT_CALL(*dtc_mock_.get(), RecordStartFailure(result));
182    EXPECT_CALL(start_callback_, Run(result, _, _));
183  }
184
185  static void SignalDone(WaitableEvent* done) {
186    done->Signal();
187  }
188
189  void WaitForDTC() {
190    WaitableEvent done(true, false);
191    BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
192        base::Bind(&SyncNonFrontendDataTypeControllerTest::SignalDone, &done));
193    done.TimedWait(TestTimeouts::action_timeout());
194    if (!done.IsSignaled()) {
195      ADD_FAILURE() << "Timed out waiting for DB thread to finish.";
196    }
197    base::MessageLoop::current()->RunUntilIdle();
198  }
199
200  void Start() {
201    non_frontend_dtc_->LoadModels(
202        base::Bind(&ModelLoadCallbackMock::Run,
203                   base::Unretained(&model_load_callback_)));
204    non_frontend_dtc_->StartAssociating(
205        base::Bind(&StartCallbackMock::Run,
206                   base::Unretained(&start_callback_)));
207  }
208
209  base::MessageLoopForUI message_loop_;
210  content::TestBrowserThread ui_thread_;
211  content::TestBrowserThread db_thread_;
212  scoped_refptr<NonFrontendDataTypeControllerFake> non_frontend_dtc_;
213  scoped_ptr<ProfileSyncComponentsFactoryMock> profile_sync_factory_;
214  scoped_refptr<NonFrontendDataTypeControllerMock> dtc_mock_;
215  ProfileMock profile_;
216  ProfileSyncServiceMock service_;
217  ModelAssociatorMock* model_associator_;
218  ChangeProcessorMock* change_processor_;
219  StartCallbackMock start_callback_;
220  ModelLoadCallbackMock model_load_callback_;
221};
222
223TEST_F(SyncNonFrontendDataTypeControllerTest, StartOk) {
224  SetStartExpectations();
225  SetAssociateExpectations();
226  SetActivateExpectations(DataTypeController::OK);
227  SetStopExpectations();
228  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
229  Start();
230  WaitForDTC();
231  EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state());
232}
233
234TEST_F(SyncNonFrontendDataTypeControllerTest, StartFirstRun) {
235  SetStartExpectations();
236  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
237      WillOnce(Return(true));
238  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
239      WillOnce(DoAll(SetArgumentPointee<0>(false), Return(true)));
240  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
241      WillOnce(Return(syncer::SyncError()));
242  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
243  SetActivateExpectations(DataTypeController::OK_FIRST_RUN);
244  SetStopExpectations();
245  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
246  Start();
247  WaitForDTC();
248  EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state());
249}
250
251TEST_F(SyncNonFrontendDataTypeControllerTest, StartAssociationFailed) {
252  SetStartExpectations();
253  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
254      WillOnce(Return(true));
255  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
256      WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
257  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
258      WillOnce(
259          Return(syncer::SyncError(FROM_HERE,
260                                   syncer::SyncError::DATATYPE_ERROR,
261                                   "Error",
262                                   syncer::BOOKMARKS)));
263  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
264  SetStartFailExpectations(DataTypeController::ASSOCIATION_FAILED);
265  // Set up association to fail with an association failed error.
266  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
267  Start();
268  WaitForDTC();
269  EXPECT_EQ(DataTypeController::DISABLED, non_frontend_dtc_->state());
270}
271
272TEST_F(SyncNonFrontendDataTypeControllerTest,
273       StartAssociationTriggersUnrecoverableError) {
274  SetStartExpectations();
275  SetStartFailExpectations(DataTypeController::UNRECOVERABLE_ERROR);
276  // Set up association to fail with an unrecoverable error.
277  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
278      WillRepeatedly(Return(true));
279  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
280      WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
281  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
282  Start();
283  WaitForDTC();
284  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
285}
286
287TEST_F(SyncNonFrontendDataTypeControllerTest, StartAssociationCryptoNotReady) {
288  SetStartExpectations();
289  SetStartFailExpectations(DataTypeController::NEEDS_CRYPTO);
290  // Set up association to fail with a NEEDS_CRYPTO error.
291  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
292      WillRepeatedly(Return(false));
293  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
294  Start();
295  WaitForDTC();
296  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
297}
298
299// Trigger a Stop() call when we check if the model associator has user created
300// nodes.
301TEST_F(SyncNonFrontendDataTypeControllerTest, AbortDuringAssociationInactive) {
302  WaitableEvent wait_for_db_thread_pause(false, false);
303  WaitableEvent pause_db_thread(false, false);
304
305  SetStartExpectations();
306  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
307      WillOnce(Return(true));
308  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
309      WillOnce(DoAll(
310          SignalEvent(&wait_for_db_thread_pause),
311          WaitOnEvent(&pause_db_thread),
312          SetArgumentPointee<0>(true),
313          Return(true)));
314  EXPECT_CALL(*model_associator_, AbortAssociation()).WillOnce(
315      SignalEvent(&pause_db_thread));
316  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
317              WillOnce(Return(syncer::SyncError()));
318  EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_,_));
319  EXPECT_CALL(*dtc_mock_.get(),
320              RecordStartFailure(DataTypeController::ABORTED));
321  SetStopExpectations();
322  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
323  Start();
324  wait_for_db_thread_pause.Wait();
325  non_frontend_dtc_->Stop();
326  WaitForDTC();
327  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
328}
329
330// Same as above but abort during the Activate call.
331TEST_F(SyncNonFrontendDataTypeControllerTest, AbortDuringAssociationActivated) {
332  WaitableEvent wait_for_association_starts(false, false);
333  WaitableEvent wait_for_dtc_stop(false, false);
334
335  SetStartExpectations();
336  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
337      WillOnce(Return(true));
338  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
339      WillOnce(DoAll(
340          SetArgumentPointee<0>(true),
341          Return(true)));
342  EXPECT_CALL(*model_associator_, AbortAssociation());
343  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
344      WillOnce(DoAll(
345          SignalEvent(&wait_for_association_starts),
346          WaitOnEvent(&wait_for_dtc_stop),
347          Return(syncer::SyncError())));
348  EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_,_));
349  EXPECT_CALL(*dtc_mock_.get(),
350              RecordStartFailure(DataTypeController::ABORTED));
351  SetStopExpectations();
352  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
353  Start();
354  wait_for_association_starts.Wait();
355  non_frontend_dtc_->Stop();
356  wait_for_dtc_stop.Signal();
357  WaitForDTC();
358  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
359}
360
361TEST_F(SyncNonFrontendDataTypeControllerTest, Stop) {
362  SetStartExpectations();
363  SetAssociateExpectations();
364  SetActivateExpectations(DataTypeController::OK);
365  SetStopExpectations();
366  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
367  Start();
368  WaitForDTC();
369  EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state());
370  non_frontend_dtc_->Stop();
371  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
372}
373
374TEST_F(SyncNonFrontendDataTypeControllerTest,
375       OnSingleDatatypeUnrecoverableError) {
376  SetStartExpectations();
377  SetAssociateExpectations();
378  SetActivateExpectations(DataTypeController::OK);
379  EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, "Test"));
380  EXPECT_CALL(service_, DisableBrokenDatatype(_, _, _))
381      .WillOnce(InvokeWithoutArgs(non_frontend_dtc_.get(),
382                                  &NonFrontendDataTypeController::Stop));
383  SetStopExpectations();
384  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
385  Start();
386  WaitForDTC();
387  EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state());
388  // This should cause non_frontend_dtc_->Stop() to be called.
389  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, base::Bind(
390      &NonFrontendDataTypeControllerFake::OnSingleDatatypeUnrecoverableError,
391      non_frontend_dtc_.get(),
392      FROM_HERE,
393      std::string("Test")));
394  WaitForDTC();
395  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
396}
397