1// Copyright (c) 2011 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 "chrome/browser/sync/glue/data_type_manager_mock.h"
8#include "chrome/browser/sync/profile_sync_service_mock.h"
9#include "chrome/browser/sync/sessions/session_state.h"
10#include "testing/gmock/include/gmock/gmock.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13using ::testing::_;
14using ::testing::Eq;
15using ::testing::Mock;
16using ::testing::NiceMock;
17using ::testing::Return;
18using ::testing::SetArgumentPointee;
19
20namespace browser_sync {
21
22using sessions::ErrorCounters;
23using sessions::SyncerStatus;
24using sessions::SyncSessionSnapshot;
25
26class BackendMigratorTest : public testing::Test {
27 public:
28  BackendMigratorTest() { }
29  virtual ~BackendMigratorTest() { }
30
31  virtual void SetUp() {
32    Mock::VerifyAndClear(manager());
33    Mock::VerifyAndClear(&service_);
34    preferred_types_.insert(syncable::BOOKMARKS);
35    preferred_types_.insert(syncable::PREFERENCES);
36    preferred_types_.insert(syncable::AUTOFILL);
37
38    ON_CALL(service_, GetPreferredDataTypes(_)).
39        WillByDefault(SetArgumentPointee<0>(preferred_types_));
40  }
41
42  void ReturnEmptyProgressMarkersInSnapshot() {
43    ReturnNonEmptyProgressMarkersInSnapshot(syncable::ModelTypeSet());
44  }
45
46  void ReturnNonEmptyProgressMarkersInSnapshot(
47      const syncable::ModelTypeSet& for_types) {
48    std::string download_progress_markers[syncable::MODEL_TYPE_COUNT];
49    for (syncable::ModelTypeSet::const_iterator it = for_types.begin();
50         it != for_types.end(); ++it) {
51      download_progress_markers[*it] = "foobar";
52    }
53
54    snap_.reset(new SyncSessionSnapshot(SyncerStatus(), ErrorCounters(),
55        0, false, syncable::ModelTypeBitSet(), download_progress_markers,
56        false, false, 0, 0, false, sessions::SyncSourceInfo()));
57    EXPECT_CALL(service_, GetLastSessionSnapshot())
58        .WillOnce(Return(snap_.get()));
59  }
60
61  void SendConfigureDone(DataTypeManager::ConfigureResult result,
62                         const syncable::ModelTypeSet& types) {
63    DataTypeManager::ConfigureResultWithErrorLocation result_with_location(
64        result, FROM_HERE, types);
65    NotificationService::current()->Notify(
66        NotificationType::SYNC_CONFIGURE_DONE,
67        Source<DataTypeManager>(&manager_),
68        Details<DataTypeManager::ConfigureResultWithErrorLocation>(
69            &result_with_location));
70  }
71
72  ProfileSyncService* service() { return &service_; }
73  DataTypeManagerMock* manager() { return &manager_; }
74  const syncable::ModelTypeSet& preferred_types() { return preferred_types_; }
75  void RemovePreferredType(syncable::ModelType type) {
76    preferred_types_.erase(type);
77    Mock::VerifyAndClear(&service_);
78    ON_CALL(service_, GetPreferredDataTypes(_)).
79        WillByDefault(SetArgumentPointee<0>(preferred_types_));
80  }
81 private:
82  scoped_ptr<SyncSessionSnapshot> snap_;
83  syncable::ModelTypeSet preferred_types_;
84  NiceMock<ProfileSyncServiceMock> service_;
85  NiceMock<DataTypeManagerMock> manager_;
86};
87
88// Test that in the normal case a migration does transition through each state
89// and wind up back in IDLE.
90TEST_F(BackendMigratorTest, Sanity) {
91  BackendMigrator migrator(service(), manager());
92  syncable::ModelTypeSet to_migrate, difference;
93  to_migrate.insert(syncable::PREFERENCES);
94  difference.insert(syncable::AUTOFILL);
95  difference.insert(syncable::BOOKMARKS);
96
97  EXPECT_CALL(*manager(), state())
98      .WillOnce(Return(DataTypeManager::CONFIGURED));
99  EXPECT_CALL(*manager(), Configure(_));
100
101  migrator.MigrateTypes(to_migrate);
102  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
103
104  SendConfigureDone(DataTypeManager::OK, difference);
105  EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
106
107  ReturnEmptyProgressMarkersInSnapshot();
108  EXPECT_CALL(*manager(), Configure(preferred_types()));
109  migrator.OnStateChanged();
110  EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state());
111
112  SendConfigureDone(DataTypeManager::OK, preferred_types());
113  EXPECT_EQ(BackendMigrator::IDLE, migrator.state());
114}
115
116// Test that the migrator waits for the data type manager to be idle before
117// starting a migration.
118TEST_F(BackendMigratorTest, WaitToStart) {
119  BackendMigrator migrator(service(), manager());
120  syncable::ModelTypeSet to_migrate;
121  to_migrate.insert(syncable::PREFERENCES);
122
123  EXPECT_CALL(*manager(), state())
124      .WillOnce(Return(DataTypeManager::CONFIGURING));
125  EXPECT_CALL(*manager(), Configure(_)).Times(0);
126  migrator.MigrateTypes(to_migrate);
127  EXPECT_EQ(BackendMigrator::WAITING_TO_START, migrator.state());
128
129  Mock::VerifyAndClearExpectations(manager());
130  EXPECT_CALL(*manager(), state())
131      .WillOnce(Return(DataTypeManager::CONFIGURED));
132  EXPECT_CALL(*manager(), Configure(_));
133  SendConfigureDone(DataTypeManager::OK, syncable::ModelTypeSet());
134
135  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
136}
137
138// Test that the migrator can cope with a migration request while a migration
139// is in progress.
140TEST_F(BackendMigratorTest, RestartMigration) {
141  BackendMigrator migrator(service(), manager());
142  syncable::ModelTypeSet to_migrate1, to_migrate2, bookmarks;
143  to_migrate1.insert(syncable::PREFERENCES);
144  to_migrate2.insert(syncable::AUTOFILL);
145  bookmarks.insert(syncable::BOOKMARKS);
146
147  EXPECT_CALL(*manager(), state())
148      .WillOnce(Return(DataTypeManager::CONFIGURED));
149  EXPECT_CALL(*manager(), Configure(_)).Times(1);
150  migrator.MigrateTypes(to_migrate1);
151
152  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
153  migrator.MigrateTypes(to_migrate2);
154
155  syncable::ModelTypeSet difference1;
156  std::set_difference(preferred_types().begin(), preferred_types().end(),
157                      to_migrate1.begin(), to_migrate1.end(),
158                      std::inserter(difference1, difference1.end()));
159
160  Mock::VerifyAndClearExpectations(manager());
161  EXPECT_CALL(*manager(), state())
162      .WillOnce(Return(DataTypeManager::CONFIGURED));
163  EXPECT_CALL(*manager(), Configure(bookmarks));
164  SendConfigureDone(DataTypeManager::OK, difference1);
165  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
166
167  SendConfigureDone(DataTypeManager::OK, bookmarks);
168  EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
169}
170
171// Test that an external invocation of Configure(...) during a migration results
172// in a migration reattempt.
173TEST_F(BackendMigratorTest, InterruptedWhileDisablingTypes) {
174  BackendMigrator migrator(service(), manager());
175  syncable::ModelTypeSet to_migrate;
176  syncable::ModelTypeSet difference;
177  to_migrate.insert(syncable::PREFERENCES);
178  difference.insert(syncable::AUTOFILL);
179  difference.insert(syncable::BOOKMARKS);
180
181  EXPECT_CALL(*manager(), state())
182      .WillOnce(Return(DataTypeManager::CONFIGURED));
183  EXPECT_CALL(*manager(), Configure(difference));
184  migrator.MigrateTypes(to_migrate);
185  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
186
187  Mock::VerifyAndClearExpectations(manager());
188  EXPECT_CALL(*manager(), state())
189      .WillOnce(Return(DataTypeManager::CONFIGURED));
190  EXPECT_CALL(*manager(), Configure(difference));
191  SendConfigureDone(DataTypeManager::OK, preferred_types());
192
193  EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
194}
195
196// Test that spurious OnStateChanged events don't confuse the migrator while
197// it's waiting for disabled types to have been purged from the sync db.
198TEST_F(BackendMigratorTest, WaitingForPurge) {
199  BackendMigrator migrator(service(), manager());
200  syncable::ModelTypeSet to_migrate, difference;
201  to_migrate.insert(syncable::PREFERENCES);
202  to_migrate.insert(syncable::AUTOFILL);
203  difference.insert(syncable::BOOKMARKS);
204
205  EXPECT_CALL(*manager(), state())
206      .WillOnce(Return(DataTypeManager::CONFIGURED));
207  EXPECT_CALL(*manager(), Configure(_));
208  migrator.MigrateTypes(to_migrate);
209  SendConfigureDone(DataTypeManager::OK, difference);
210  EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
211
212  ReturnNonEmptyProgressMarkersInSnapshot(to_migrate);
213  migrator.OnStateChanged();
214  EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
215
216  syncable::ModelTypeSet prefs;
217  prefs.insert(syncable::PREFERENCES);
218  ReturnNonEmptyProgressMarkersInSnapshot(prefs);
219  migrator.OnStateChanged();
220  EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
221
222  syncable::ModelTypeSet bookmarks;
223  bookmarks.insert(syncable::BOOKMARKS);
224  ReturnNonEmptyProgressMarkersInSnapshot(bookmarks);
225  EXPECT_CALL(*manager(), Configure(preferred_types()));
226  migrator.OnStateChanged();
227  EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state());
228}
229
230TEST_F(BackendMigratorTest, MigratedTypeDisabledByUserDuringMigration) {
231  BackendMigrator migrator(service(), manager());
232  syncable::ModelTypeSet to_migrate;
233  to_migrate.insert(syncable::PREFERENCES);
234
235  EXPECT_CALL(*manager(), state())
236      .WillOnce(Return(DataTypeManager::CONFIGURED));
237  EXPECT_CALL(*manager(), Configure(_));
238  migrator.MigrateTypes(to_migrate);
239
240  RemovePreferredType(syncable::PREFERENCES);
241  SendConfigureDone(DataTypeManager::OK, preferred_types());
242  EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
243
244  Mock::VerifyAndClearExpectations(manager());
245  ReturnEmptyProgressMarkersInSnapshot();
246  EXPECT_CALL(*manager(), Configure(preferred_types()));
247  migrator.OnStateChanged();
248
249  EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state());
250  SendConfigureDone(DataTypeManager::OK, preferred_types());
251  EXPECT_EQ(BackendMigrator::IDLE, migrator.state());
252}
253
254TEST_F(BackendMigratorTest, ConfigureFailure) {
255  BackendMigrator migrator(service(), manager());
256  syncable::ModelTypeSet to_migrate;
257  to_migrate.insert(syncable::PREFERENCES);
258
259  EXPECT_CALL(*manager(), state())
260      .WillOnce(Return(DataTypeManager::CONFIGURED));
261  EXPECT_CALL(*manager(), Configure(_)).Times(1);
262  migrator.MigrateTypes(to_migrate);
263  SendConfigureDone(DataTypeManager::ABORTED, syncable::ModelTypeSet());
264  EXPECT_EQ(BackendMigrator::IDLE, migrator.state());
265}
266
267};  // namespace browser_sync
268