non_frontend_data_type_controller_unittest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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.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 void CreateSyncComponents() OVERRIDE {
73    ProfileSyncComponentsFactory::SyncComponents sync_components =
74        profile_sync_factory()->
75            CreateBookmarkSyncComponents(profile_sync_service(), this);
76    set_model_associator(sync_components.model_associator);
77    set_change_processor(sync_components.change_processor);
78  }
79
80  virtual bool PostTaskOnBackendThread(
81      const tracked_objects::Location& from_here,
82      const base::Closure& task) OVERRIDE {
83    return BrowserThread::PostTask(BrowserThread::DB, from_here, task);
84  }
85
86  // We mock the following methods because their default implementations do
87  // nothing, but we still want to make sure they're called appropriately.
88  virtual bool StartModels() OVERRIDE {
89    return mock_->StartModels();
90  }
91  virtual void StopModels() OVERRIDE {
92    mock_->StopModels();
93  }
94  virtual void RecordUnrecoverableError(
95      const tracked_objects::Location& from_here,
96      const std::string& message) OVERRIDE {
97    mock_->RecordUnrecoverableError(from_here, message);
98  }
99  virtual void RecordAssociationTime(base::TimeDelta time) OVERRIDE {
100    mock_->RecordAssociationTime(time);
101  }
102  virtual void RecordStartFailure(
103      DataTypeController::StartResult result) OVERRIDE {
104    mock_->RecordStartFailure(result);
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        model_associator_(NULL),
116        change_processor_(NULL) {}
117
118  virtual void SetUp() {
119    db_thread_.Start();
120    profile_sync_factory_.reset(
121        new StrictMock<ProfileSyncComponentsFactoryMock>());
122
123    // All of these are refcounted, so don't need to be released.
124    dtc_mock_ = new StrictMock<NonFrontendDataTypeControllerMock>();
125    non_frontend_dtc_ =
126        new NonFrontendDataTypeControllerFake(profile_sync_factory_.get(),
127                                              &profile_,
128                                              &service_,
129                                              dtc_mock_.get());
130  }
131
132  virtual void TearDown() {
133    db_thread_.Stop();
134  }
135
136 protected:
137  void SetStartExpectations() {
138    EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(true));
139    EXPECT_CALL(model_load_callback_, Run(_, _));
140    model_associator_ = new ModelAssociatorMock();
141    change_processor_ = new ChangeProcessorMock();
142    EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _)).
143        WillOnce(Return(ProfileSyncComponentsFactory::SyncComponents(
144            model_associator_, change_processor_)));
145  }
146
147  void SetAssociateExpectations() {
148    EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
149        WillOnce(Return(true));
150    EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
151        WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
152    EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
153        WillOnce(Return(syncer::SyncError()));
154    EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
155  }
156
157  void SetActivateExpectations(DataTypeController::StartResult result) {
158    EXPECT_CALL(service_, ActivateDataType(_, _, _));
159    EXPECT_CALL(start_callback_, Run(result, _, _));
160  }
161
162  void SetStopExpectations() {
163    EXPECT_CALL(*dtc_mock_.get(), StopModels());
164    EXPECT_CALL(service_, DeactivateDataType(_));
165    EXPECT_CALL(*model_associator_, DisassociateModels()).
166                WillOnce(Return(syncer::SyncError()));
167  }
168
169  void SetStartFailExpectations(DataTypeController::StartResult result) {
170    EXPECT_CALL(*dtc_mock_.get(), StopModels());
171    if (DataTypeController::IsUnrecoverableResult(result))
172      EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, _));
173    if (model_associator_) {
174      EXPECT_CALL(*model_associator_, DisassociateModels()).
175                  WillOnce(Return(syncer::SyncError()));
176    }
177    EXPECT_CALL(*dtc_mock_.get(), RecordStartFailure(result));
178    EXPECT_CALL(start_callback_, Run(result, _, _));
179  }
180
181  static void SignalDone(WaitableEvent* done) {
182    done->Signal();
183  }
184
185  void WaitForDTC() {
186    WaitableEvent done(true, false);
187    BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
188        base::Bind(&SyncNonFrontendDataTypeControllerTest::SignalDone, &done));
189    done.TimedWait(TestTimeouts::action_timeout());
190    if (!done.IsSignaled()) {
191      ADD_FAILURE() << "Timed out waiting for DB thread to finish.";
192    }
193    base::MessageLoop::current()->RunUntilIdle();
194  }
195
196  void Start() {
197    non_frontend_dtc_->LoadModels(
198        base::Bind(&ModelLoadCallbackMock::Run,
199                   base::Unretained(&model_load_callback_)));
200    non_frontend_dtc_->StartAssociating(
201        base::Bind(&StartCallbackMock::Run,
202                   base::Unretained(&start_callback_)));
203  }
204
205  base::MessageLoopForUI message_loop_;
206  content::TestBrowserThread ui_thread_;
207  content::TestBrowserThread db_thread_;
208  scoped_refptr<NonFrontendDataTypeControllerFake> non_frontend_dtc_;
209  scoped_ptr<ProfileSyncComponentsFactoryMock> profile_sync_factory_;
210  scoped_refptr<NonFrontendDataTypeControllerMock> dtc_mock_;
211  ProfileMock profile_;
212  ProfileSyncServiceMock service_;
213  ModelAssociatorMock* model_associator_;
214  ChangeProcessorMock* change_processor_;
215  StartCallbackMock start_callback_;
216  ModelLoadCallbackMock model_load_callback_;
217};
218
219TEST_F(SyncNonFrontendDataTypeControllerTest, StartOk) {
220  SetStartExpectations();
221  SetAssociateExpectations();
222  SetActivateExpectations(DataTypeController::OK);
223  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
224  Start();
225  WaitForDTC();
226  EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state());
227}
228
229TEST_F(SyncNonFrontendDataTypeControllerTest, StartFirstRun) {
230  SetStartExpectations();
231  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
232      WillOnce(Return(true));
233  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
234      WillOnce(DoAll(SetArgumentPointee<0>(false), Return(true)));
235  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
236      WillOnce(Return(syncer::SyncError()));
237  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
238  SetActivateExpectations(DataTypeController::OK_FIRST_RUN);
239  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
240  Start();
241  WaitForDTC();
242  EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state());
243}
244
245TEST_F(SyncNonFrontendDataTypeControllerTest, StartAssociationFailed) {
246  SetStartExpectations();
247  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
248      WillOnce(Return(true));
249  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
250      WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
251  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
252      WillOnce(
253          Return(syncer::SyncError(FROM_HERE,
254                                   syncer::SyncError::DATATYPE_ERROR,
255                                   "Error",
256                                   syncer::BOOKMARKS)));
257  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
258  SetStartFailExpectations(DataTypeController::ASSOCIATION_FAILED);
259  // Set up association to fail with an association failed error.
260  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
261  Start();
262  WaitForDTC();
263  EXPECT_EQ(DataTypeController::DISABLED, non_frontend_dtc_->state());
264}
265
266TEST_F(SyncNonFrontendDataTypeControllerTest,
267       StartAssociationTriggersUnrecoverableError) {
268  SetStartExpectations();
269  SetStartFailExpectations(DataTypeController::UNRECOVERABLE_ERROR);
270  // Set up association to fail with an unrecoverable error.
271  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
272      WillRepeatedly(Return(true));
273  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
274      WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
275  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
276  Start();
277  WaitForDTC();
278  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
279}
280
281TEST_F(SyncNonFrontendDataTypeControllerTest, StartAssociationCryptoNotReady) {
282  SetStartExpectations();
283  SetStartFailExpectations(DataTypeController::NEEDS_CRYPTO);
284  // Set up association to fail with a NEEDS_CRYPTO error.
285  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
286      WillRepeatedly(Return(false));
287  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
288  Start();
289  WaitForDTC();
290  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
291}
292
293// Trigger a Stop() call when we check if the model associator has user created
294// nodes.
295TEST_F(SyncNonFrontendDataTypeControllerTest, AbortDuringAssociationInactive) {
296  WaitableEvent wait_for_db_thread_pause(false, false);
297  WaitableEvent pause_db_thread(false, false);
298
299  SetStartExpectations();
300  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
301      WillOnce(Return(true));
302  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
303      WillOnce(DoAll(
304          SignalEvent(&wait_for_db_thread_pause),
305          WaitOnEvent(&pause_db_thread),
306          SetArgumentPointee<0>(true),
307          Return(true)));
308  EXPECT_CALL(*model_associator_, AbortAssociation()).WillOnce(
309      SignalEvent(&pause_db_thread));
310  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
311              WillOnce(Return(syncer::SyncError()));
312  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
313  EXPECT_CALL(service_, ActivateDataType(_, _, _));
314  EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_,_));
315  EXPECT_CALL(*dtc_mock_.get(),
316              RecordStartFailure(DataTypeController::ABORTED));
317  SetStopExpectations();
318  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
319  Start();
320  wait_for_db_thread_pause.Wait();
321  non_frontend_dtc_->Stop();
322  WaitForDTC();
323  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
324}
325
326// Same as above but abort during the Activate call.
327TEST_F(SyncNonFrontendDataTypeControllerTest, AbortDuringAssociationActivated) {
328  WaitableEvent wait_for_db_thread_pause(false, false);
329  WaitableEvent pause_db_thread(false, false);
330
331  SetStartExpectations();
332  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
333      WillOnce(Return(true));
334  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
335      WillOnce(DoAll(
336          SetArgumentPointee<0>(true),
337          Return(true)));
338  EXPECT_CALL(*model_associator_, AbortAssociation()).WillOnce(
339      SignalEvent(&pause_db_thread));
340  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
341      WillOnce(Return(syncer::SyncError()));
342  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
343  EXPECT_CALL(service_, ActivateDataType(_, _, _)).WillOnce(DoAll(
344      SignalEvent(&wait_for_db_thread_pause), WaitOnEvent(&pause_db_thread)));
345  EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED,_,_));
346  EXPECT_CALL(*dtc_mock_.get(),
347              RecordStartFailure(DataTypeController::ABORTED));
348  SetStopExpectations();
349  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
350  Start();
351  wait_for_db_thread_pause.Wait();
352  non_frontend_dtc_->Stop();
353  WaitForDTC();
354  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
355}
356
357TEST_F(SyncNonFrontendDataTypeControllerTest, Stop) {
358  SetStartExpectations();
359  SetAssociateExpectations();
360  SetActivateExpectations(DataTypeController::OK);
361  SetStopExpectations();
362  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
363  Start();
364  WaitForDTC();
365  EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state());
366  non_frontend_dtc_->Stop();
367  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
368}
369
370TEST_F(SyncNonFrontendDataTypeControllerTest,
371       OnSingleDatatypeUnrecoverableError) {
372  SetStartExpectations();
373  SetAssociateExpectations();
374  SetActivateExpectations(DataTypeController::OK);
375  EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, "Test"));
376  EXPECT_CALL(service_, DisableBrokenDatatype(_, _, _))
377      .WillOnce(InvokeWithoutArgs(non_frontend_dtc_.get(),
378                                  &NonFrontendDataTypeController::Stop));
379  SetStopExpectations();
380  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
381  Start();
382  WaitForDTC();
383  EXPECT_EQ(DataTypeController::RUNNING, non_frontend_dtc_->state());
384  // This should cause non_frontend_dtc_->Stop() to be called.
385  BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, base::Bind(
386      &NonFrontendDataTypeControllerFake::OnSingleDatatypeUnrecoverableError,
387      non_frontend_dtc_.get(),
388      FROM_HERE,
389      std::string("Test")));
390  WaitForDTC();
391  EXPECT_EQ(DataTypeController::NOT_RUNNING, non_frontend_dtc_->state());
392}
393