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
18using syncable::WriteTransaction;
19
20namespace browser_sync {
21namespace sessions {
22namespace {
23
24class SyncSessionTest : public testing::Test,
25                        public SyncSession::Delegate,
26                        public ModelSafeWorkerRegistrar {
27 public:
28  SyncSessionTest() : controller_invocations_allowed_(false) {
29    GetModelSafeRoutingInfo(&routes_);
30  }
31
32  SyncSession* MakeSession() {
33    return new SyncSession(context_.get(), this, SyncSourceInfo(), routes_,
34                           std::vector<ModelSafeWorker*>());
35  }
36
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  }
45  virtual void TearDown() {
46    session_.reset();
47    context_.reset();
48  }
49
50  virtual void OnSilencedUntil(const base::TimeTicks& silenced_until) {
51    FailControllerInvocationIfDisabled("OnSilencedUntil");
52  }
53  virtual bool IsSyncingCurrentlySilenced() {
54    FailControllerInvocationIfDisabled("IsSyncingCurrentlySilenced");
55    return false;
56  }
57  virtual void OnReceivedLongPollIntervalUpdate(
58      const base::TimeDelta& new_interval) {
59    FailControllerInvocationIfDisabled("OnReceivedLongPollIntervalUpdate");
60  }
61  virtual void OnReceivedShortPollIntervalUpdate(
62      const base::TimeDelta& new_interval) {
63    FailControllerInvocationIfDisabled("OnReceivedShortPollIntervalUpdate");
64  }
65  virtual void OnShouldStopSyncingPermanently() {
66    FailControllerInvocationIfDisabled("OnShouldStopSyncingPermanently");
67  }
68
69  // ModelSafeWorkerRegistrar implementation.
70  virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {}
71  virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
72    out->swap(routes_);
73  }
74
75  StatusController* status() { return session_->status_controller(); }
76 protected:
77  void FailControllerInvocationIfDisabled(const std::string& msg) {
78    if (!controller_invocations_allowed_)
79      FAIL() << msg;
80  }
81
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
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
101TEST_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
111TEST_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
133TEST_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
153TEST_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
160TEST_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
172TEST_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
187TEST_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
201TEST_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
218TEST_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
233TEST_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
248TEST_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
256TEST_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
266TEST_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
304TEST_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
324TEST_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
336TEST_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