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/backend_migrator.h"
6
7#include "base/message_loop/message_loop.h"
8#include "base/tracked_objects.h"
9#include "chrome/browser/chrome_notification_types.h"
10#include "chrome/browser/sync/glue/data_type_manager_mock.h"
11#include "chrome/browser/sync/profile_sync_service_mock.h"
12#include "sync/internal_api/public/base/model_type_test_util.h"
13#include "sync/internal_api/public/test/test_user_share.h"
14#include "sync/internal_api/public/write_transaction.h"
15#include "sync/protocol/sync.pb.h"
16#include "sync/syncable/directory.h"  // TODO(tim): Remove. Bug 131130.
17#include "testing/gmock/include/gmock/gmock.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using ::testing::_;
21using ::testing::Eq;
22using ::testing::Mock;
23using ::testing::NiceMock;
24using ::testing::Return;
25
26namespace browser_sync {
27
28using syncer::sessions::SyncSessionSnapshot;
29
30class SyncBackendMigratorTest : public testing::Test {
31 public:
32  SyncBackendMigratorTest() : service_(&profile_) { }
33  virtual ~SyncBackendMigratorTest() { }
34
35  virtual void SetUp() {
36    test_user_share_.SetUp();
37    Mock::VerifyAndClear(manager());
38    Mock::VerifyAndClear(&service_);
39    preferred_types_.Put(syncer::BOOKMARKS);
40    preferred_types_.Put(syncer::PREFERENCES);
41    preferred_types_.Put(syncer::AUTOFILL);
42
43    ON_CALL(service_, GetPreferredDataTypes()).
44        WillByDefault(Return(preferred_types_));
45
46    migrator_.reset(
47        new BackendMigrator(
48            "Profile0", test_user_share_.user_share(), service(), manager(),
49            base::Closure()));
50    SetUnsyncedTypes(syncer::ModelTypeSet());
51  }
52
53  virtual void TearDown() {
54    migrator_.reset();
55    test_user_share_.TearDown();
56  }
57
58  // Marks all types in |unsynced_types| as unsynced  and all other
59  // types as synced.
60  void SetUnsyncedTypes(syncer::ModelTypeSet unsynced_types) {
61    syncer::WriteTransaction trans(FROM_HERE,
62                                     test_user_share_.user_share());
63    for (int i = syncer::FIRST_REAL_MODEL_TYPE;
64         i < syncer::MODEL_TYPE_COUNT; ++i) {
65      syncer::ModelType type = syncer::ModelTypeFromInt(i);
66      sync_pb::DataTypeProgressMarker progress_marker;
67      if (!unsynced_types.Has(type)) {
68        progress_marker.set_token("dummy");
69      }
70      trans.GetDirectory()->SetDownloadProgress(type, progress_marker);
71    }
72  }
73
74  void SendConfigureDone(DataTypeManager::ConfigureStatus status,
75                         syncer::ModelTypeSet requested_types) {
76    if (status == DataTypeManager::OK) {
77      DataTypeManager::ConfigureResult result(status, requested_types);
78      migrator_->OnConfigureDone(result);
79    } else {
80      std::map<syncer::ModelType, syncer::SyncError> errors;
81      DataTypeManager::ConfigureResult result(
82          status,
83          requested_types,
84          errors,
85          syncer::ModelTypeSet(),
86          syncer::ModelTypeSet());
87      migrator_->OnConfigureDone(result);
88    }
89    message_loop_.RunUntilIdle();
90  }
91
92  ProfileSyncService* service() { return &service_; }
93  DataTypeManagerMock* manager() { return &manager_; }
94  syncer::ModelTypeSet preferred_types() { return preferred_types_; }
95  BackendMigrator* migrator() { return migrator_.get(); }
96  void RemovePreferredType(syncer::ModelType type) {
97    preferred_types_.Remove(type);
98    Mock::VerifyAndClear(&service_);
99    ON_CALL(service_, GetPreferredDataTypes()).
100        WillByDefault(Return(preferred_types_));
101  }
102
103 private:
104  scoped_ptr<SyncSessionSnapshot> snap_;
105  base::MessageLoop message_loop_;
106  syncer::ModelTypeSet preferred_types_;
107  TestingProfile profile_;
108  NiceMock<ProfileSyncServiceMock> service_;
109  NiceMock<DataTypeManagerMock> manager_;
110  syncer::TestUserShare test_user_share_;
111  scoped_ptr<BackendMigrator> migrator_;
112};
113
114class MockMigrationObserver : public MigrationObserver {
115 public:
116  virtual ~MockMigrationObserver() {}
117
118  MOCK_METHOD0(OnMigrationStateChange, void());
119};
120
121// Test that in the normal case a migration does transition through each state
122// and wind up back in IDLE.
123TEST_F(SyncBackendMigratorTest, Sanity) {
124  MockMigrationObserver migration_observer;
125  migrator()->AddMigrationObserver(&migration_observer);
126  EXPECT_CALL(migration_observer, OnMigrationStateChange()).Times(4);
127
128  syncer::ModelTypeSet to_migrate, difference;
129  to_migrate.Put(syncer::PREFERENCES);
130  difference.Put(syncer::AUTOFILL);
131  difference.Put(syncer::BOOKMARKS);
132
133  EXPECT_CALL(*manager(), state())
134      .WillOnce(Return(DataTypeManager::CONFIGURED));
135  EXPECT_CALL(
136      *manager(),
137      PurgeForMigration(_, syncer::CONFIGURE_REASON_MIGRATION)).Times(1);
138  EXPECT_CALL(
139      *manager(),
140      Configure(_, syncer::CONFIGURE_REASON_MIGRATION)).Times(1);
141
142  migrator()->MigrateTypes(to_migrate);
143  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
144
145  SetUnsyncedTypes(to_migrate);
146  SendConfigureDone(DataTypeManager::OK, difference);
147  EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator()->state());
148
149  SetUnsyncedTypes(syncer::ModelTypeSet());
150  SendConfigureDone(DataTypeManager::OK, preferred_types());
151  EXPECT_EQ(BackendMigrator::IDLE, migrator()->state());
152
153  migrator()->RemoveMigrationObserver(&migration_observer);
154}
155
156// Test that in the normal case with Nigori a migration transitions through
157// each state and wind up back in IDLE.
158TEST_F(SyncBackendMigratorTest, MigrateNigori) {
159  syncer::ModelTypeSet to_migrate, difference;
160  to_migrate.Put(syncer::NIGORI);
161  difference.Put(syncer::AUTOFILL);
162  difference.Put(syncer::BOOKMARKS);
163
164  EXPECT_CALL(*manager(), state())
165      .WillOnce(Return(DataTypeManager::CONFIGURED));
166
167  EXPECT_CALL(*manager(), PurgeForMigration(_,
168      syncer::CONFIGURE_REASON_MIGRATION));
169
170  migrator()->MigrateTypes(to_migrate);
171  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
172
173  SetUnsyncedTypes(to_migrate);
174  SendConfigureDone(DataTypeManager::OK, difference);
175  EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator()->state());
176
177  SetUnsyncedTypes(syncer::ModelTypeSet());
178  SendConfigureDone(DataTypeManager::OK, preferred_types());
179  EXPECT_EQ(BackendMigrator::IDLE, migrator()->state());
180}
181
182
183// Test that the migrator waits for the data type manager to be idle before
184// starting a migration.
185TEST_F(SyncBackendMigratorTest, WaitToStart) {
186  syncer::ModelTypeSet to_migrate;
187  to_migrate.Put(syncer::PREFERENCES);
188
189  EXPECT_CALL(*manager(), state())
190      .WillOnce(Return(DataTypeManager::CONFIGURING));
191  EXPECT_CALL(*manager(), Configure(_, _)).Times(0);
192  migrator()->MigrateTypes(to_migrate);
193  EXPECT_EQ(BackendMigrator::WAITING_TO_START, migrator()->state());
194
195  Mock::VerifyAndClearExpectations(manager());
196  EXPECT_CALL(*manager(), state())
197      .WillOnce(Return(DataTypeManager::CONFIGURED));
198  EXPECT_CALL(*manager(),
199              PurgeForMigration(_, syncer::CONFIGURE_REASON_MIGRATION));
200  SetUnsyncedTypes(syncer::ModelTypeSet());
201  SendConfigureDone(DataTypeManager::OK, syncer::ModelTypeSet());
202
203  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
204}
205
206// Test that the migrator can cope with a migration request while a migration
207// is in progress.
208TEST_F(SyncBackendMigratorTest, RestartMigration) {
209  syncer::ModelTypeSet to_migrate1, to_migrate2, to_migrate_union, bookmarks;
210  to_migrate1.Put(syncer::PREFERENCES);
211  to_migrate2.Put(syncer::AUTOFILL);
212  to_migrate_union.Put(syncer::PREFERENCES);
213  to_migrate_union.Put(syncer::AUTOFILL);
214  bookmarks.Put(syncer::BOOKMARKS);
215
216  EXPECT_CALL(*manager(), state())
217      .WillOnce(Return(DataTypeManager::CONFIGURED));
218  EXPECT_CALL(
219      *manager(),
220      PurgeForMigration(_, syncer::CONFIGURE_REASON_MIGRATION)).Times(2);
221  migrator()->MigrateTypes(to_migrate1);
222
223  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
224  migrator()->MigrateTypes(to_migrate2);
225
226  const syncer::ModelTypeSet difference1 =
227      Difference(preferred_types(), to_migrate1);
228
229  Mock::VerifyAndClearExpectations(manager());
230  EXPECT_CALL(
231      *manager(),
232      PurgeForMigration(_, syncer::CONFIGURE_REASON_MIGRATION)).Times(1);
233  EXPECT_CALL(*manager(), Configure(_, syncer::CONFIGURE_REASON_MIGRATION))
234      .Times(1);
235  SetUnsyncedTypes(to_migrate1);
236  SendConfigureDone(DataTypeManager::OK, difference1);
237  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
238
239  SetUnsyncedTypes(to_migrate_union);
240  SendConfigureDone(DataTypeManager::OK, bookmarks);
241  EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator()->state());
242}
243
244// Test that an external invocation of Configure(...) during a migration results
245// in a migration reattempt.
246TEST_F(SyncBackendMigratorTest, InterruptedWhileDisablingTypes) {
247  syncer::ModelTypeSet to_migrate;
248  syncer::ModelTypeSet difference;
249  to_migrate.Put(syncer::PREFERENCES);
250  difference.Put(syncer::AUTOFILL);
251  difference.Put(syncer::BOOKMARKS);
252
253  EXPECT_CALL(*manager(), state())
254      .WillOnce(Return(DataTypeManager::CONFIGURED));
255  EXPECT_CALL(*manager(), PurgeForMigration(HasModelTypes(to_migrate),
256      syncer::CONFIGURE_REASON_MIGRATION));
257  migrator()->MigrateTypes(to_migrate);
258  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
259
260  Mock::VerifyAndClearExpectations(manager());
261  EXPECT_CALL(*manager(), PurgeForMigration(HasModelTypes(to_migrate),
262      syncer::CONFIGURE_REASON_MIGRATION));
263  SetUnsyncedTypes(syncer::ModelTypeSet());
264  SendConfigureDone(DataTypeManager::OK, preferred_types());
265
266  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
267}
268
269// Test that spurious OnConfigureDone events don't confuse the
270// migrator while it's waiting for disabled types to have been purged
271// from the sync db.
272TEST_F(SyncBackendMigratorTest, WaitingForPurge) {
273  syncer::ModelTypeSet to_migrate, difference;
274  to_migrate.Put(syncer::PREFERENCES);
275  to_migrate.Put(syncer::AUTOFILL);
276  difference.Put(syncer::BOOKMARKS);
277
278  EXPECT_CALL(*manager(), state())
279      .WillOnce(Return(DataTypeManager::CONFIGURED));
280  EXPECT_CALL(
281      *manager(),
282      PurgeForMigration(_, syncer::CONFIGURE_REASON_MIGRATION)).Times(1);
283  EXPECT_CALL(
284      *manager(),
285      Configure(_, syncer::CONFIGURE_REASON_MIGRATION)).Times(1);
286
287  migrator()->MigrateTypes(to_migrate);
288  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
289
290  SendConfigureDone(DataTypeManager::OK, difference);
291  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
292
293  syncer::ModelTypeSet prefs;
294  prefs.Put(syncer::PREFERENCES);
295  SetUnsyncedTypes(prefs);
296  SendConfigureDone(DataTypeManager::OK, difference);
297  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator()->state());
298
299  SetUnsyncedTypes(to_migrate);
300  SendConfigureDone(DataTypeManager::OK, difference);
301  EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator()->state());
302}
303
304TEST_F(SyncBackendMigratorTest, MigratedTypeDisabledByUserDuringMigration) {
305  syncer::ModelTypeSet to_migrate;
306  to_migrate.Put(syncer::PREFERENCES);
307
308  EXPECT_CALL(*manager(), state())
309      .WillOnce(Return(DataTypeManager::CONFIGURED));
310  EXPECT_CALL(
311      *manager(),
312      PurgeForMigration(_, syncer::CONFIGURE_REASON_MIGRATION)).Times(1);
313  EXPECT_CALL(
314      *manager(),
315      Configure(_, syncer::CONFIGURE_REASON_MIGRATION)).Times(1);
316  migrator()->MigrateTypes(to_migrate);
317
318  RemovePreferredType(syncer::PREFERENCES);
319  SetUnsyncedTypes(to_migrate);
320  SendConfigureDone(DataTypeManager::OK, preferred_types());
321  EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator()->state());
322  SetUnsyncedTypes(syncer::ModelTypeSet());
323  SendConfigureDone(DataTypeManager::OK, preferred_types());
324  EXPECT_EQ(BackendMigrator::IDLE, migrator()->state());
325}
326
327TEST_F(SyncBackendMigratorTest, ConfigureFailure) {
328  syncer::ModelTypeSet to_migrate;
329  to_migrate.Put(syncer::PREFERENCES);
330
331  EXPECT_CALL(*manager(), state())
332      .WillOnce(Return(DataTypeManager::CONFIGURED));
333  EXPECT_CALL(
334      *manager(),
335      PurgeForMigration(_, syncer::CONFIGURE_REASON_MIGRATION)).Times(1);
336  migrator()->MigrateTypes(to_migrate);
337  SetUnsyncedTypes(syncer::ModelTypeSet());
338  SendConfigureDone(DataTypeManager::ABORTED, syncer::ModelTypeSet());
339  EXPECT_EQ(BackendMigrator::IDLE, migrator()->state());
340}
341
342};  // namespace browser_sync
343