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/memory/scoped_ptr.h"
11#include "base/message_loop/message_loop.h"
12#include "base/tracked_objects.h"
13#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
14#include "chrome/browser/sync/glue/frontend_data_type_controller_mock.h"
15#include "chrome/browser/sync/profile_sync_components_factory_mock.h"
16#include "chrome/browser/sync/profile_sync_service_mock.h"
17#include "chrome/test/base/profile_mock.h"
18#include "components/sync_driver/change_processor_mock.h"
19#include "components/sync_driver/data_type_controller_mock.h"
20#include "components/sync_driver/model_associator_mock.h"
21#include "content/public/test/test_browser_thread_bundle.h"
22
23using browser_sync::FrontendDataTypeController;
24using browser_sync::FrontendDataTypeControllerMock;
25using sync_driver::ChangeProcessorMock;
26using sync_driver::DataTypeController;
27using sync_driver::ModelAssociatorMock;
28using sync_driver::ModelLoadCallbackMock;
29using sync_driver::StartCallbackMock;
30using testing::_;
31using testing::DoAll;
32using testing::InvokeWithoutArgs;
33using testing::Return;
34using testing::SetArgumentPointee;
35using testing::StrictMock;
36
37class FrontendDataTypeControllerFake : public FrontendDataTypeController {
38 public:
39  FrontendDataTypeControllerFake(
40      ProfileSyncComponentsFactory* profile_sync_factory,
41      Profile* profile,
42      ProfileSyncService* sync_service,
43      FrontendDataTypeControllerMock* mock)
44      : FrontendDataTypeController(base::MessageLoopProxy::current(),
45                                   base::Closure(),
46                                   profile_sync_factory,
47                                   profile,
48                                   sync_service),
49        mock_(mock) {}
50  virtual syncer::ModelType type() const OVERRIDE { return syncer::BOOKMARKS; }
51
52 private:
53  virtual void CreateSyncComponents() OVERRIDE {
54    ProfileSyncComponentsFactory::SyncComponents sync_components =
55        profile_sync_factory_->
56            CreateBookmarkSyncComponents(sync_service_, this);
57    model_associator_.reset(sync_components.model_associator);
58    change_processor_.reset(sync_components.change_processor);
59  }
60
61  // We mock the following methods because their default implementations do
62  // nothing, but we still want to make sure they're called appropriately.
63  virtual bool StartModels() OVERRIDE {
64    return mock_->StartModels();
65  }
66  virtual void CleanUpState() OVERRIDE {
67    mock_->CleanUpState();
68  }
69  virtual void RecordUnrecoverableError(
70      const tracked_objects::Location& from_here,
71      const std::string& message) OVERRIDE {
72    mock_->RecordUnrecoverableError(from_here, message);
73  }
74  virtual void RecordAssociationTime(base::TimeDelta time) OVERRIDE {
75    mock_->RecordAssociationTime(time);
76  }
77  virtual void RecordStartFailure(
78      DataTypeController::ConfigureResult result) OVERRIDE {
79    mock_->RecordStartFailure(result);
80  }
81 private:
82  virtual ~FrontendDataTypeControllerFake() {}
83  FrontendDataTypeControllerMock* mock_;
84};
85
86class SyncFrontendDataTypeControllerTest : public testing::Test {
87 public:
88  SyncFrontendDataTypeControllerTest()
89      : thread_bundle_(content::TestBrowserThreadBundle::DEFAULT),
90        service_(&profile_) {}
91
92  virtual void SetUp() {
93    profile_sync_factory_.reset(new ProfileSyncComponentsFactoryMock());
94    dtc_mock_ = new StrictMock<FrontendDataTypeControllerMock>();
95    frontend_dtc_ =
96        new FrontendDataTypeControllerFake(profile_sync_factory_.get(),
97                                           &profile_,
98                                           &service_,
99                                           dtc_mock_.get());
100  }
101
102 protected:
103  void SetStartExpectations() {
104    EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(true));
105    EXPECT_CALL(model_load_callback_, Run(_, _));
106    model_associator_ = new ModelAssociatorMock();
107    change_processor_ = new ChangeProcessorMock();
108    EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _)).
109        WillOnce(Return(ProfileSyncComponentsFactory::SyncComponents(
110            model_associator_, change_processor_)));
111  }
112
113  void SetAssociateExpectations() {
114    EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
115        WillOnce(Return(true));
116    EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
117        WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
118    EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
119        WillOnce(Return(syncer::SyncError()));
120    EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
121  }
122
123  void SetActivateExpectations(DataTypeController::ConfigureResult result) {
124    EXPECT_CALL(start_callback_, Run(result, _, _));
125  }
126
127  void SetStopExpectations() {
128    EXPECT_CALL(*dtc_mock_.get(), CleanUpState());
129    EXPECT_CALL(service_, DeactivateDataType(_));
130    EXPECT_CALL(*model_associator_, DisassociateModels()).
131                WillOnce(Return(syncer::SyncError()));
132  }
133
134  void SetStartFailExpectations(DataTypeController::ConfigureResult result) {
135    if (DataTypeController::IsUnrecoverableResult(result))
136      EXPECT_CALL(*dtc_mock_.get(), RecordUnrecoverableError(_, _));
137    EXPECT_CALL(*dtc_mock_.get(), CleanUpState());
138    EXPECT_CALL(*dtc_mock_.get(), RecordStartFailure(result));
139    EXPECT_CALL(start_callback_, Run(result, _, _));
140  }
141
142  void Start() {
143    frontend_dtc_->LoadModels(
144        base::Bind(&ModelLoadCallbackMock::Run,
145                   base::Unretained(&model_load_callback_)));
146    frontend_dtc_->StartAssociating(
147        base::Bind(&StartCallbackMock::Run,
148                   base::Unretained(&start_callback_)));
149  }
150
151  void PumpLoop() { base::MessageLoop::current()->RunUntilIdle(); }
152
153  content::TestBrowserThreadBundle thread_bundle_;
154  scoped_refptr<FrontendDataTypeControllerFake> frontend_dtc_;
155  scoped_ptr<ProfileSyncComponentsFactoryMock> profile_sync_factory_;
156  scoped_refptr<FrontendDataTypeControllerMock> dtc_mock_;
157  ProfileMock profile_;
158  ProfileSyncServiceMock service_;
159  ModelAssociatorMock* model_associator_;
160  ChangeProcessorMock* change_processor_;
161  StartCallbackMock start_callback_;
162  ModelLoadCallbackMock model_load_callback_;
163};
164
165TEST_F(SyncFrontendDataTypeControllerTest, StartOk) {
166  SetStartExpectations();
167  SetAssociateExpectations();
168  SetActivateExpectations(DataTypeController::OK);
169  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
170  Start();
171  EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state());
172}
173
174TEST_F(SyncFrontendDataTypeControllerTest, StartFirstRun) {
175  SetStartExpectations();
176  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
177      WillOnce(Return(true));
178  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
179      WillOnce(DoAll(SetArgumentPointee<0>(false), Return(true)));
180  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
181      WillOnce(Return(syncer::SyncError()));
182  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
183  SetActivateExpectations(DataTypeController::OK_FIRST_RUN);
184  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
185  Start();
186  EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state());
187}
188
189TEST_F(SyncFrontendDataTypeControllerTest, AbortDuringStartModels) {
190  EXPECT_CALL(*dtc_mock_.get(), StartModels()).WillOnce(Return(false));
191  EXPECT_CALL(*dtc_mock_.get(), CleanUpState());
192  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
193  frontend_dtc_->LoadModels(
194      base::Bind(&ModelLoadCallbackMock::Run,
195                 base::Unretained(&model_load_callback_)));
196  EXPECT_EQ(DataTypeController::MODEL_STARTING, frontend_dtc_->state());
197  frontend_dtc_->Stop();
198  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
199}
200
201TEST_F(SyncFrontendDataTypeControllerTest, StartAssociationFailed) {
202  SetStartExpectations();
203  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
204      WillOnce(Return(true));
205  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
206      WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
207  EXPECT_CALL(*model_associator_, AssociateModels(_, _)).
208      WillOnce(Return(syncer::SyncError(FROM_HERE,
209                                        syncer::SyncError::DATATYPE_ERROR,
210                                        "error",
211                                        syncer::BOOKMARKS)));
212
213  EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_));
214  SetStartFailExpectations(DataTypeController::ASSOCIATION_FAILED);
215  // Set up association to fail with an association failed error.
216  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
217  Start();
218  EXPECT_EQ(DataTypeController::DISABLED, frontend_dtc_->state());
219}
220
221TEST_F(SyncFrontendDataTypeControllerTest,
222       StartAssociationTriggersUnrecoverableError) {
223  SetStartExpectations();
224  SetStartFailExpectations(DataTypeController::UNRECOVERABLE_ERROR);
225  // Set up association to fail with an unrecoverable error.
226  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
227      WillRepeatedly(Return(true));
228  EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
229      WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
230  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
231  Start();
232  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
233}
234
235TEST_F(SyncFrontendDataTypeControllerTest, StartAssociationCryptoNotReady) {
236  SetStartExpectations();
237  SetStartFailExpectations(DataTypeController::NEEDS_CRYPTO);
238  // Set up association to fail with a NEEDS_CRYPTO error.
239  EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
240      WillRepeatedly(Return(false));
241  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
242  Start();
243  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
244}
245
246TEST_F(SyncFrontendDataTypeControllerTest, Stop) {
247  SetStartExpectations();
248  SetAssociateExpectations();
249  SetActivateExpectations(DataTypeController::OK);
250  SetStopExpectations();
251  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
252  Start();
253  EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state());
254  frontend_dtc_->Stop();
255  EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
256}
257