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/sessions/sync_session.h"
6
7 #include "base/memory/ref_counted.h"
8 #include "chrome/browser/sync/engine/conflict_resolver.h"
9 #include "chrome/browser/sync/engine/mock_model_safe_workers.h"
10 #include "chrome/browser/sync/engine/syncer_types.h"
11 #include "chrome/browser/sync/engine/syncer_util.h"
12 #include "chrome/browser/sync/syncable/directory_manager.h"
13 #include "chrome/browser/sync/syncable/model_type.h"
14 #include "chrome/browser/sync/syncable/syncable.h"
15 #include "chrome/test/sync/engine/test_directory_setter_upper.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 using syncable::WriteTransaction;
19
20 namespace browser_sync {
21 namespace sessions {
22 namespace {
23
24 class SyncSessionTest : public testing::Test,
25 public SyncSession::Delegate,
26 public ModelSafeWorkerRegistrar {
27 public:
SyncSessionTest()28 SyncSessionTest() : controller_invocations_allowed_(false) {
29 GetModelSafeRoutingInfo(&routes_);
30 }
31
MakeSession()32 SyncSession* MakeSession() {
33 return new SyncSession(context_.get(), this, SyncSourceInfo(), routes_,
34 std::vector<ModelSafeWorker*>());
35 }
36
SetUp()37 virtual void SetUp() {
38 context_.reset(new SyncSessionContext(NULL, NULL, this,
39 std::vector<SyncEngineEventListener*>()));
40 routes_.clear();
41 routes_[syncable::BOOKMARKS] = GROUP_UI;
42 routes_[syncable::AUTOFILL] = GROUP_UI;
43 session_.reset(MakeSession());
44 }
TearDown()45 virtual void TearDown() {
46 session_.reset();
47 context_.reset();
48 }
49
OnSilencedUntil(const base::TimeTicks & silenced_until)50 virtual void OnSilencedUntil(const base::TimeTicks& silenced_until) {
51 FailControllerInvocationIfDisabled("OnSilencedUntil");
52 }
IsSyncingCurrentlySilenced()53 virtual bool IsSyncingCurrentlySilenced() {
54 FailControllerInvocationIfDisabled("IsSyncingCurrentlySilenced");
55 return false;
56 }
OnReceivedLongPollIntervalUpdate(const base::TimeDelta & new_interval)57 virtual void OnReceivedLongPollIntervalUpdate(
58 const base::TimeDelta& new_interval) {
59 FailControllerInvocationIfDisabled("OnReceivedLongPollIntervalUpdate");
60 }
OnReceivedShortPollIntervalUpdate(const base::TimeDelta & new_interval)61 virtual void OnReceivedShortPollIntervalUpdate(
62 const base::TimeDelta& new_interval) {
63 FailControllerInvocationIfDisabled("OnReceivedShortPollIntervalUpdate");
64 }
OnShouldStopSyncingPermanently()65 virtual void OnShouldStopSyncingPermanently() {
66 FailControllerInvocationIfDisabled("OnShouldStopSyncingPermanently");
67 }
68
69 // ModelSafeWorkerRegistrar implementation.
GetWorkers(std::vector<ModelSafeWorker * > * out)70 virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {}
GetModelSafeRoutingInfo(ModelSafeRoutingInfo * out)71 virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
72 out->swap(routes_);
73 }
74
status()75 StatusController* status() { return session_->status_controller(); }
76 protected:
FailControllerInvocationIfDisabled(const std::string & msg)77 void FailControllerInvocationIfDisabled(const std::string& msg) {
78 if (!controller_invocations_allowed_)
79 FAIL() << msg;
80 }
81
ParamsMeaningAllEnabledTypes()82 syncable::ModelTypeBitSet ParamsMeaningAllEnabledTypes() {
83 syncable::ModelTypeBitSet request_params;
84 request_params[syncable::BOOKMARKS] = true;
85 request_params[syncable::AUTOFILL] = true;
86 return request_params;
87 }
88
ParamsMeaningJustOneEnabledType()89 syncable::ModelTypeBitSet ParamsMeaningJustOneEnabledType() {
90 syncable::ModelTypeBitSet request_params;
91 request_params[syncable::AUTOFILL] = true;
92 return request_params;
93 }
94
95 bool controller_invocations_allowed_;
96 scoped_ptr<SyncSession> session_;
97 scoped_ptr<SyncSessionContext> context_;
98 ModelSafeRoutingInfo routes_;
99 };
100
TEST_F(SyncSessionTest,ScopedContextHelpers)101 TEST_F(SyncSessionTest, ScopedContextHelpers) {
102 ConflictResolver resolver;
103 EXPECT_FALSE(context_->resolver());
104 {
105 ScopedSessionContextConflictResolver s_resolver(context_.get(), &resolver);
106 EXPECT_EQ(&resolver, context_->resolver());
107 }
108 EXPECT_FALSE(context_->resolver());
109 }
110
TEST_F(SyncSessionTest,SetWriteTransaction)111 TEST_F(SyncSessionTest, SetWriteTransaction) {
112 TestDirectorySetterUpper db;
113 db.SetUp();
114 session_.reset();
115 context_.reset(new SyncSessionContext(NULL, db.manager(), this,
116 std::vector<SyncEngineEventListener*>()));
117 session_.reset(MakeSession());
118 context_->set_account_name(db.name());
119 syncable::ScopedDirLookup dir(context_->directory_manager(),
120 context_->account_name());
121 ASSERT_TRUE(dir.good());
122
123 scoped_ptr<SyncSession> session(MakeSession());
124 EXPECT_TRUE(NULL == session->write_transaction());
125 {
126 WriteTransaction trans(dir, syncable::UNITTEST, __FILE__, __LINE__);
127 sessions::ScopedSetSessionWriteTransaction set_trans(session.get(), &trans);
128 EXPECT_TRUE(&trans == session->write_transaction());
129 }
130 db.TearDown();
131 }
132
TEST_F(SyncSessionTest,MoreToSyncIfUnsyncedGreaterThanCommitted)133 TEST_F(SyncSessionTest, MoreToSyncIfUnsyncedGreaterThanCommitted) {
134 // If any forward progress was made during the session, and the number of
135 // unsynced handles still exceeds the number of commit ids we added, there is
136 // more to sync. For example, this occurs if we had more commit ids
137 // than could fit in a single commit batch.
138 EXPECT_FALSE(session_->HasMoreToSync());
139 OrderedCommitSet commit_set(routes_);
140 commit_set.AddCommitItem(0, syncable::Id(), syncable::BOOKMARKS);
141 status()->set_commit_set(commit_set);
142 EXPECT_FALSE(session_->HasMoreToSync());
143
144 std::vector<int64> unsynced_handles;
145 unsynced_handles.push_back(1);
146 unsynced_handles.push_back(2);
147 status()->set_unsynced_handles(unsynced_handles);
148 EXPECT_FALSE(session_->HasMoreToSync());
149 status()->increment_num_successful_commits();
150 EXPECT_TRUE(session_->HasMoreToSync());
151 }
152
TEST_F(SyncSessionTest,MoreToSyncIfConflictSetsBuilt)153 TEST_F(SyncSessionTest, MoreToSyncIfConflictSetsBuilt) {
154 // If we built conflict sets, then we need to loop back and try
155 // to get updates & commit again.
156 status()->update_conflict_sets_built(true);
157 EXPECT_TRUE(session_->HasMoreToSync());
158 }
159
TEST_F(SyncSessionTest,MoreToDownloadIfDownloadFailed)160 TEST_F(SyncSessionTest, MoreToDownloadIfDownloadFailed) {
161 status()->set_updates_request_types(ParamsMeaningAllEnabledTypes());
162
163 // When DownloadUpdatesCommand fails, these should be false.
164 EXPECT_FALSE(status()->ServerSaysNothingMoreToDownload());
165 EXPECT_FALSE(status()->download_updates_succeeded());
166
167 // Download updates has its own loop in the syncer; it shouldn't factor
168 // into HasMoreToSync.
169 EXPECT_FALSE(session_->HasMoreToSync());
170 }
171
TEST_F(SyncSessionTest,MoreToDownloadIfGotChangesRemaining)172 TEST_F(SyncSessionTest, MoreToDownloadIfGotChangesRemaining) {
173 status()->set_updates_request_types(ParamsMeaningAllEnabledTypes());
174
175 // When the server returns changes_remaining, that means there's
176 // more to download.
177 status()->mutable_updates_response()->mutable_get_updates()
178 ->set_changes_remaining(1000L);
179 EXPECT_FALSE(status()->ServerSaysNothingMoreToDownload());
180 EXPECT_TRUE(status()->download_updates_succeeded());
181
182 // Download updates has its own loop in the syncer; it shouldn't factor
183 // into HasMoreToSync.
184 EXPECT_FALSE(session_->HasMoreToSync());
185 }
186
TEST_F(SyncSessionTest,MoreToDownloadIfGotNoChangesRemaining)187 TEST_F(SyncSessionTest, MoreToDownloadIfGotNoChangesRemaining) {
188 status()->set_updates_request_types(ParamsMeaningAllEnabledTypes());
189
190 // When the server returns a timestamp, that means we're up to date.
191 status()->mutable_updates_response()->mutable_get_updates()
192 ->set_changes_remaining(0);
193 EXPECT_TRUE(status()->ServerSaysNothingMoreToDownload());
194 EXPECT_TRUE(status()->download_updates_succeeded());
195
196 // Download updates has its own loop in the syncer; it shouldn't factor
197 // into HasMoreToSync.
198 EXPECT_FALSE(session_->HasMoreToSync());
199 }
200
TEST_F(SyncSessionTest,MoreToDownloadIfGotNoChangesRemainingForSubset)201 TEST_F(SyncSessionTest, MoreToDownloadIfGotNoChangesRemainingForSubset) {
202 status()->set_updates_request_types(ParamsMeaningJustOneEnabledType());
203
204 // When the server returns a timestamp, that means we're up to date for that
205 // type. But there may still be more to download if there are other
206 // datatypes that we didn't request on this go-round.
207 status()->mutable_updates_response()->mutable_get_updates()
208 ->set_changes_remaining(0);
209
210 EXPECT_TRUE(status()->ServerSaysNothingMoreToDownload());
211 EXPECT_TRUE(status()->download_updates_succeeded());
212
213 // Download updates has its own loop in the syncer; it shouldn't factor
214 // into HasMoreToSync.
215 EXPECT_FALSE(session_->HasMoreToSync());
216 }
217
TEST_F(SyncSessionTest,MoreToDownloadIfGotChangesRemainingAndEntries)218 TEST_F(SyncSessionTest, MoreToDownloadIfGotChangesRemainingAndEntries) {
219 status()->set_updates_request_types(ParamsMeaningAllEnabledTypes());
220 // The actual entry count should not factor into the HasMoreToSync
221 // determination.
222 status()->mutable_updates_response()->mutable_get_updates()->add_entries();
223 status()->mutable_updates_response()->mutable_get_updates()
224 ->set_changes_remaining(1000000L);;
225 EXPECT_FALSE(status()->ServerSaysNothingMoreToDownload());
226 EXPECT_TRUE(status()->download_updates_succeeded());
227
228 // Download updates has its own loop in the syncer; it shouldn't factor
229 // into HasMoreToSync.
230 EXPECT_FALSE(session_->HasMoreToSync());
231 }
232
TEST_F(SyncSessionTest,MoreToDownloadIfGotNoChangesRemainingAndEntries)233 TEST_F(SyncSessionTest, MoreToDownloadIfGotNoChangesRemainingAndEntries) {
234 status()->set_updates_request_types(ParamsMeaningAllEnabledTypes());
235 // The actual entry count should not factor into the HasMoreToSync
236 // determination.
237 status()->mutable_updates_response()->mutable_get_updates()->add_entries();
238 status()->mutable_updates_response()->mutable_get_updates()
239 ->set_changes_remaining(0);
240 EXPECT_TRUE(status()->ServerSaysNothingMoreToDownload());
241 EXPECT_TRUE(status()->download_updates_succeeded());
242
243 // Download updates has its own loop in the syncer; it shouldn't factor
244 // into HasMoreToSync.
245 EXPECT_FALSE(session_->HasMoreToSync());
246 }
247
TEST_F(SyncSessionTest,MoreToSyncIfConflictsResolved)248 TEST_F(SyncSessionTest, MoreToSyncIfConflictsResolved) {
249 // Conflict resolution happens after get updates and commit,
250 // so we need to loop back and get updates / commit again now
251 // that we have made forward progress.
252 status()->update_conflicts_resolved(true);
253 EXPECT_TRUE(session_->HasMoreToSync());
254 }
255
TEST_F(SyncSessionTest,ResetTransientState)256 TEST_F(SyncSessionTest, ResetTransientState) {
257 status()->update_conflicts_resolved(true);
258 status()->increment_num_successful_commits();
259 EXPECT_TRUE(session_->HasMoreToSync());
260 session_->ResetTransientState();
261 EXPECT_FALSE(status()->conflicts_resolved());
262 EXPECT_FALSE(session_->HasMoreToSync());
263 EXPECT_FALSE(status()->TestAndClearIsDirty());
264 }
265
TEST_F(SyncSessionTest,Coalesce)266 TEST_F(SyncSessionTest, Coalesce) {
267 std::vector<ModelSafeWorker*> workers_one, workers_two;
268 ModelSafeRoutingInfo routes_one, routes_two;
269 syncable::ModelTypePayloadMap one_type =
270 syncable::ModelTypePayloadMapFromBitSet(
271 ParamsMeaningJustOneEnabledType(),
272 std::string());
273 syncable::ModelTypePayloadMap all_types =
274 syncable::ModelTypePayloadMapFromBitSet(
275 ParamsMeaningAllEnabledTypes(),
276 std::string());
277 SyncSourceInfo source_one(sync_pb::GetUpdatesCallerInfo::PERIODIC, one_type);
278 SyncSourceInfo source_two(sync_pb::GetUpdatesCallerInfo::LOCAL, all_types);
279
280 scoped_refptr<MockDBModelWorker> db_worker(new MockDBModelWorker());
281 scoped_refptr<MockUIModelWorker> ui_worker(new MockUIModelWorker());
282 workers_one.push_back(db_worker);
283 workers_two.push_back(db_worker);
284 workers_two.push_back(ui_worker);
285 routes_one[syncable::AUTOFILL] = GROUP_DB;
286 routes_two[syncable::AUTOFILL] = GROUP_DB;
287 routes_two[syncable::BOOKMARKS] = GROUP_UI;
288 SyncSession one(context_.get(), this, source_one, routes_one, workers_one);
289 SyncSession two(context_.get(), this, source_two, routes_two, workers_two);
290
291 one.Coalesce(two);
292
293 EXPECT_EQ(two.source().updates_source, one.source().updates_source);
294 EXPECT_EQ(all_types, one.source().types);
295 std::vector<ModelSafeWorker*>::const_iterator it_db =
296 std::find(one.workers().begin(), one.workers().end(), db_worker);
297 std::vector<ModelSafeWorker*>::const_iterator it_ui =
298 std::find(one.workers().begin(), one.workers().end(), ui_worker);
299 EXPECT_NE(it_db, one.workers().end());
300 EXPECT_NE(it_ui, one.workers().end());
301 EXPECT_EQ(routes_two, one.routing_info());
302 }
303
TEST_F(SyncSessionTest,MakeTypePayloadMapFromBitSet)304 TEST_F(SyncSessionTest, MakeTypePayloadMapFromBitSet) {
305 syncable::ModelTypeBitSet types;
306 std::string payload = "test";
307 syncable::ModelTypePayloadMap types_with_payloads =
308 syncable::ModelTypePayloadMapFromBitSet(types,
309 payload);
310 EXPECT_TRUE(types_with_payloads.empty());
311
312 types[syncable::BOOKMARKS] = true;
313 types[syncable::PASSWORDS] = true;
314 types[syncable::AUTOFILL] = true;
315 payload = "test2";
316 types_with_payloads = syncable::ModelTypePayloadMapFromBitSet(types, payload);
317
318 ASSERT_EQ(3U, types_with_payloads.size());
319 EXPECT_EQ(types_with_payloads[syncable::BOOKMARKS], payload);
320 EXPECT_EQ(types_with_payloads[syncable::PASSWORDS], payload);
321 EXPECT_EQ(types_with_payloads[syncable::AUTOFILL], payload);
322 }
323
TEST_F(SyncSessionTest,MakeTypePayloadMapFromRoutingInfo)324 TEST_F(SyncSessionTest, MakeTypePayloadMapFromRoutingInfo) {
325 std::string payload = "test";
326 syncable::ModelTypePayloadMap types_with_payloads
327 = syncable::ModelTypePayloadMapFromRoutingInfo(routes_, payload);
328 ASSERT_EQ(routes_.size(), types_with_payloads.size());
329 for (ModelSafeRoutingInfo::iterator iter = routes_.begin();
330 iter != routes_.end();
331 ++iter) {
332 EXPECT_EQ(payload, types_with_payloads[iter->first]);
333 }
334 }
335
TEST_F(SyncSessionTest,CoalescePayloads)336 TEST_F(SyncSessionTest, CoalescePayloads) {
337 syncable::ModelTypePayloadMap original;
338 std::string empty_payload;
339 std::string payload1 = "payload1";
340 std::string payload2 = "payload2";
341 std::string payload3 = "payload3";
342 original[syncable::BOOKMARKS] = empty_payload;
343 original[syncable::PASSWORDS] = payload1;
344 original[syncable::AUTOFILL] = payload2;
345 original[syncable::THEMES] = payload3;
346
347 syncable::ModelTypePayloadMap update;
348 update[syncable::BOOKMARKS] = empty_payload; // Same.
349 update[syncable::PASSWORDS] = empty_payload; // Overwrite with empty.
350 update[syncable::AUTOFILL] = payload1; // Overwrite with non-empty.
351 update[syncable::SESSIONS] = payload2; // New.
352 // Themes untouched.
353
354 CoalescePayloads(&original, update);
355 ASSERT_EQ(5U, original.size());
356 EXPECT_EQ(empty_payload, original[syncable::BOOKMARKS]);
357 EXPECT_EQ(payload1, original[syncable::PASSWORDS]);
358 EXPECT_EQ(payload1, original[syncable::AUTOFILL]);
359 EXPECT_EQ(payload2, original[syncable::SESSIONS]);
360 EXPECT_EQ(payload3, original[syncable::THEMES]);
361 }
362
363 } // namespace
364 } // namespace sessions
365 } // namespace browser_sync
366