Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(166)

Side by Side Diff: chrome/browser/sync/engine/syncer_unittest.cc

Issue 9699057: [Sync] Move 'sync' target to sync/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Tim's comments Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/sync/engine/syncer_types.cc ('k') | chrome/browser/sync/engine/syncer_util.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 // Syncer unit tests. Unfortunately a lot of these tests
6 // are outdated and need to be reworked and updated.
7
8 #include <algorithm>
9 #include <limits>
10 #include <list>
11 #include <map>
12 #include <set>
13 #include <string>
14
15 #include "base/bind.h"
16 #include "base/bind_helpers.h"
17 #include "base/callback.h"
18 #include "base/compiler_specific.h"
19 #include "base/location.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/message_loop.h"
22 #include "base/string_number_conversions.h"
23 #include "base/stringprintf.h"
24 #include "base/time.h"
25 #include "build/build_config.h"
26 #include "chrome/browser/sync/engine/get_commit_ids_command.h"
27 #include "chrome/browser/sync/engine/model_safe_worker.h"
28 #include "chrome/browser/sync/engine/net/server_connection_manager.h"
29 #include "chrome/browser/sync/engine/nigori_util.h"
30 #include "chrome/browser/sync/engine/process_updates_command.h"
31 #include "chrome/browser/sync/engine/syncer.h"
32 #include "chrome/browser/sync/engine/syncer_proto_util.h"
33 #include "chrome/browser/sync/engine/syncer_util.h"
34 #include "chrome/browser/sync/engine/syncproto.h"
35 #include "chrome/browser/sync/sessions/sync_session_context.h"
36 #include "chrome/browser/sync/syncable/model_type.h"
37 #include "chrome/browser/sync/syncable/syncable.h"
38 #include "chrome/browser/sync/test/engine/fake_model_worker.h"
39 #include "chrome/browser/sync/test/engine/mock_connection_manager.h"
40 #include "chrome/browser/sync/test/engine/test_directory_setter_upper.h"
41 #include "chrome/browser/sync/test/engine/test_id_factory.h"
42 #include "chrome/browser/sync/test/engine/test_syncable_utils.h"
43 #include "chrome/browser/sync/test/fake_encryptor.h"
44 #include "chrome/browser/sync/test/fake_extensions_activity_monitor.h"
45 #include "chrome/browser/sync/util/cryptographer.h"
46 #include "chrome/browser/sync/util/time.h"
47 #include "sync/protocol/bookmark_specifics.pb.h"
48 #include "sync/protocol/nigori_specifics.pb.h"
49 #include "sync/protocol/preference_specifics.pb.h"
50 #include "sync/protocol/sync.pb.h"
51 #include "testing/gtest/include/gtest/gtest.h"
52
53 using base::TimeDelta;
54
55 using std::map;
56 using std::multimap;
57 using std::set;
58 using std::string;
59
60 namespace browser_sync {
61
62 using syncable::BaseTransaction;
63 using syncable::Blob;
64 using syncable::CountEntriesWithName;
65 using syncable::Directory;
66 using syncable::Entry;
67 using syncable::GetFirstEntryWithName;
68 using syncable::GetOnlyEntryWithName;
69 using syncable::Id;
70 using syncable::kEncryptedString;
71 using syncable::MutableEntry;
72 using syncable::ReadTransaction;
73 using syncable::WriteTransaction;
74
75 using syncable::BASE_VERSION;
76 using syncable::CREATE;
77 using syncable::CREATE_NEW_UPDATE_ITEM;
78 using syncable::GET_BY_HANDLE;
79 using syncable::GET_BY_ID;
80 using syncable::GET_BY_CLIENT_TAG;
81 using syncable::GET_BY_SERVER_TAG;
82 using syncable::ID;
83 using syncable::IS_DEL;
84 using syncable::IS_DIR;
85 using syncable::IS_UNAPPLIED_UPDATE;
86 using syncable::IS_UNSYNCED;
87 using syncable::META_HANDLE;
88 using syncable::MTIME;
89 using syncable::NEXT_ID;
90 using syncable::NON_UNIQUE_NAME;
91 using syncable::PARENT_ID;
92 using syncable::PREV_ID;
93 using syncable::BASE_SERVER_SPECIFICS;
94 using syncable::SERVER_IS_DEL;
95 using syncable::SERVER_NON_UNIQUE_NAME;
96 using syncable::SERVER_PARENT_ID;
97 using syncable::SERVER_POSITION_IN_PARENT;
98 using syncable::SERVER_SPECIFICS;
99 using syncable::SERVER_VERSION;
100 using syncable::UNIQUE_CLIENT_TAG;
101 using syncable::UNIQUE_SERVER_TAG;
102 using syncable::SPECIFICS;
103 using syncable::SYNCING;
104 using syncable::UNITTEST;
105
106 using sessions::ConflictProgress;
107 using sessions::ScopedSetSessionWriteTransaction;
108 using sessions::StatusController;
109 using sessions::SyncSessionContext;
110 using sessions::SyncSession;
111
112 class SyncerTest : public testing::Test,
113 public SyncSession::Delegate,
114 public ModelSafeWorkerRegistrar,
115 public SyncEngineEventListener {
116 protected:
117 SyncerTest() : syncer_(NULL), saw_syncer_event_(false) {}
118
119 // SyncSession::Delegate implementation.
120 virtual void OnSilencedUntil(const base::TimeTicks& silenced_until) OVERRIDE {
121 FAIL() << "Should not get silenced.";
122 }
123 virtual bool IsSyncingCurrentlySilenced() OVERRIDE {
124 return false;
125 }
126 virtual void OnReceivedLongPollIntervalUpdate(
127 const base::TimeDelta& new_interval) OVERRIDE {
128 last_long_poll_interval_received_ = new_interval;
129 }
130 virtual void OnReceivedShortPollIntervalUpdate(
131 const base::TimeDelta& new_interval) OVERRIDE {
132 last_short_poll_interval_received_ = new_interval;
133 }
134 virtual void OnReceivedSessionsCommitDelay(
135 const base::TimeDelta& new_delay) OVERRIDE {
136 last_sessions_commit_delay_seconds_ = new_delay;
137 }
138 virtual void OnShouldStopSyncingPermanently() OVERRIDE {
139 }
140 virtual void OnSyncProtocolError(
141 const sessions::SyncSessionSnapshot& snapshot) OVERRIDE {
142 }
143
144 // ModelSafeWorkerRegistrar implementation.
145 virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) OVERRIDE {
146 out->push_back(worker_.get());
147 }
148
149 virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) OVERRIDE {
150 // We're just testing the sync engine here, so we shunt everything to
151 // the SyncerThread. Datatypes which aren't enabled aren't in the map.
152 for (syncable::ModelTypeSet::Iterator it = enabled_datatypes_.First();
153 it.Good(); it.Inc()) {
154 (*out)[it.Get()] = GROUP_PASSIVE;
155 }
156 }
157
158 virtual void OnSyncEngineEvent(const SyncEngineEvent& event) OVERRIDE {
159 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event.what_happened;
160 // we only test for entry-specific events, not status changed ones.
161 switch (event.what_happened) {
162 case SyncEngineEvent::SYNC_CYCLE_BEGIN: // Fall through.
163 case SyncEngineEvent::STATUS_CHANGED:
164 case SyncEngineEvent::SYNC_CYCLE_ENDED:
165 return;
166 default:
167 CHECK(false) << "Handling unknown error type in unit tests!!";
168 }
169 saw_syncer_event_ = true;
170 }
171
172 SyncSession* MakeSession() {
173 ModelSafeRoutingInfo info;
174 std::vector<ModelSafeWorker*> workers;
175 GetModelSafeRoutingInfo(&info);
176 GetWorkers(&workers);
177 syncable::ModelTypePayloadMap types =
178 syncable::ModelTypePayloadMapFromRoutingInfo(info, std::string());
179 return new SyncSession(context_.get(), this,
180 sessions::SyncSourceInfo(sync_pb::GetUpdatesCallerInfo::UNKNOWN, types),
181 info, workers);
182 }
183
184 bool SyncShareAsDelegate() {
185 session_.reset(MakeSession());
186 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
187 return session_->HasMoreToSync();
188 }
189
190 void LoopSyncShare() {
191 bool should_loop = false;
192 int loop_iterations = 0;
193 do {
194 ASSERT_LT(++loop_iterations, 100) << "infinite loop detected. please fix";
195 should_loop = SyncShareAsDelegate();
196 } while (should_loop);
197 }
198
199 virtual void SetUp() {
200 dir_maker_.SetUp();
201 mock_server_.reset(new MockConnectionManager(directory()));
202 EnableDatatype(syncable::BOOKMARKS);
203 EnableDatatype(syncable::NIGORI);
204 EnableDatatype(syncable::PREFERENCES);
205 EnableDatatype(syncable::NIGORI);
206 worker_ = new FakeModelWorker(GROUP_PASSIVE);
207 std::vector<SyncEngineEventListener*> listeners;
208 listeners.push_back(this);
209 context_.reset(
210 new SyncSessionContext(
211 mock_server_.get(), directory(), this,
212 &extensions_activity_monitor_, listeners, NULL));
213 ASSERT_FALSE(context_->resolver());
214 syncer_ = new Syncer();
215 session_.reset(MakeSession());
216
217 ReadTransaction trans(FROM_HERE, directory());
218 syncable::Directory::ChildHandles children;
219 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
220 ASSERT_EQ(0u, children.size());
221 saw_syncer_event_ = false;
222 root_id_ = TestIdFactory::root();
223 parent_id_ = ids_.MakeServer("parent id");
224 child_id_ = ids_.MakeServer("child id");
225 }
226
227 virtual void TearDown() {
228 mock_server_.reset();
229 delete syncer_;
230 syncer_ = NULL;
231 dir_maker_.TearDown();
232 }
233 void WriteTestDataToEntry(WriteTransaction* trans, MutableEntry* entry) {
234 EXPECT_FALSE(entry->Get(IS_DIR));
235 EXPECT_FALSE(entry->Get(IS_DEL));
236 sync_pb::EntitySpecifics specifics;
237 specifics.mutable_bookmark()->set_url("http://demo/");
238 specifics.mutable_bookmark()->set_favicon("PNG");
239 entry->Put(syncable::SPECIFICS, specifics);
240 entry->Put(syncable::IS_UNSYNCED, true);
241 }
242 void VerifyTestDataInEntry(BaseTransaction* trans, Entry* entry) {
243 EXPECT_FALSE(entry->Get(IS_DIR));
244 EXPECT_FALSE(entry->Get(IS_DEL));
245 VerifyTestBookmarkDataInEntry(entry);
246 }
247 void VerifyTestBookmarkDataInEntry(Entry* entry) {
248 const sync_pb::EntitySpecifics& specifics = entry->Get(syncable::SPECIFICS);
249 EXPECT_TRUE(specifics.has_bookmark());
250 EXPECT_EQ("PNG", specifics.bookmark().favicon());
251 EXPECT_EQ("http://demo/", specifics.bookmark().url());
252 }
253
254 void SyncRepeatedlyToTriggerConflictResolution(SyncSession* session) {
255 // We should trigger after less than 6 syncs, but extra does no harm.
256 for (int i = 0 ; i < 6 ; ++i)
257 syncer_->SyncShare(session, SYNCER_BEGIN, SYNCER_END);
258 }
259 void SyncRepeatedlyToTriggerStuckSignal(SyncSession* session) {
260 // We should trigger after less than 10 syncs, but we want to avoid brittle
261 // tests.
262 for (int i = 0 ; i < 12 ; ++i)
263 syncer_->SyncShare(session, SYNCER_BEGIN, SYNCER_END);
264 }
265 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
266 sync_pb::EntitySpecifics result;
267 AddDefaultFieldValue(syncable::BOOKMARKS, &result);
268 return result;
269 }
270
271 sync_pb::EntitySpecifics DefaultPreferencesSpecifics() {
272 sync_pb::EntitySpecifics result;
273 AddDefaultFieldValue(syncable::PREFERENCES, &result);
274 return result;
275 }
276 // Enumeration of alterations to entries for commit ordering tests.
277 enum EntryFeature {
278 LIST_END = 0, // Denotes the end of the list of features from below.
279 SYNCED, // Items are unsynced by default
280 DELETED,
281 OLD_MTIME,
282 MOVED_FROM_ROOT,
283 };
284
285 struct CommitOrderingTest {
286 // expected commit index.
287 int commit_index;
288 // Details about the item
289 syncable::Id id;
290 syncable::Id parent_id;
291 EntryFeature features[10];
292
293 static const CommitOrderingTest LAST_COMMIT_ITEM;
294 };
295
296 void RunCommitOrderingTest(CommitOrderingTest* test) {
297 map<int, syncable::Id> expected_positions;
298 { // Transaction scope.
299 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
300 while (!test->id.IsRoot()) {
301 if (test->commit_index >= 0) {
302 map<int, syncable::Id>::value_type entry(test->commit_index,
303 test->id);
304 bool double_position = !expected_positions.insert(entry).second;
305 ASSERT_FALSE(double_position) << "Two id's expected at one position";
306 }
307 string utf8_name = test->id.GetServerId();
308 string name(utf8_name.begin(), utf8_name.end());
309 MutableEntry entry(&trans, CREATE, test->parent_id, name);
310
311 entry.Put(syncable::ID, test->id);
312 if (test->id.ServerKnows()) {
313 entry.Put(BASE_VERSION, 5);
314 entry.Put(SERVER_VERSION, 5);
315 entry.Put(SERVER_PARENT_ID, test->parent_id);
316 }
317 entry.Put(syncable::IS_DIR, true);
318 entry.Put(syncable::IS_UNSYNCED, true);
319 entry.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
320 // Set the time to 30 seconds in the future to reduce the chance of
321 // flaky tests.
322 const base::Time& now_plus_30s =
323 base::Time::Now() + base::TimeDelta::FromSeconds(30);
324 const base::Time& now_minus_2h =
325 base::Time::Now() - base::TimeDelta::FromHours(2);
326 entry.Put(syncable::MTIME, now_plus_30s);
327 for (size_t i = 0 ; i < arraysize(test->features) ; ++i) {
328 switch (test->features[i]) {
329 case LIST_END:
330 break;
331 case SYNCED:
332 entry.Put(syncable::IS_UNSYNCED, false);
333 break;
334 case DELETED:
335 entry.Put(syncable::IS_DEL, true);
336 break;
337 case OLD_MTIME:
338 entry.Put(MTIME, now_minus_2h);
339 break;
340 case MOVED_FROM_ROOT:
341 entry.Put(SERVER_PARENT_ID, trans.root_id());
342 break;
343 default:
344 FAIL() << "Bad value in CommitOrderingTest list";
345 }
346 }
347 test++;
348 }
349 }
350 LoopSyncShare();
351 ASSERT_TRUE(expected_positions.size() ==
352 mock_server_->committed_ids().size());
353 // If this test starts failing, be aware other sort orders could be valid.
354 for (size_t i = 0; i < expected_positions.size(); ++i) {
355 EXPECT_EQ(1u, expected_positions.count(i));
356 EXPECT_TRUE(expected_positions[i] == mock_server_->committed_ids()[i]);
357 }
358 }
359
360 void DoTruncationTest(const vector<int64>& unsynced_handle_view,
361 const vector<syncable::Id>& expected_id_order) {
362 for (size_t limit = expected_id_order.size() + 2; limit > 0; --limit) {
363 StatusController* status = session_->mutable_status_controller();
364 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
365 ScopedSetSessionWriteTransaction set_trans(session_.get(), &wtrans);
366
367 ModelSafeRoutingInfo routes;
368 GetModelSafeRoutingInfo(&routes);
369 GetCommitIdsCommand command(limit);
370 std::set<int64> ready_unsynced_set;
371 command.FilterUnreadyEntries(&wtrans, syncable::ModelTypeSet(),
372 syncable::ModelTypeSet(), false,
373 unsynced_handle_view, &ready_unsynced_set);
374 command.BuildCommitIds(session_->write_transaction(), routes,
375 ready_unsynced_set);
376 syncable::Directory::UnsyncedMetaHandles ready_unsynced_vector(
377 ready_unsynced_set.begin(), ready_unsynced_set.end());
378 status->set_unsynced_handles(ready_unsynced_vector);
379 vector<syncable::Id> output =
380 command.ordered_commit_set_->GetAllCommitIds();
381 size_t truncated_size = std::min(limit, expected_id_order.size());
382 ASSERT_EQ(truncated_size, output.size());
383 for (size_t i = 0; i < truncated_size; ++i) {
384 ASSERT_EQ(expected_id_order[i], output[i])
385 << "At index " << i << " with batch size limited to " << limit;
386 }
387 sessions::OrderedCommitSet::Projection proj;
388 proj = command.ordered_commit_set_->GetCommitIdProjection(GROUP_PASSIVE);
389 ASSERT_EQ(truncated_size, proj.size());
390 for (size_t i = 0; i < truncated_size; ++i) {
391 SCOPED_TRACE(::testing::Message("Projection mismatch with i = ") << i);
392 syncable::Id projected =
393 command.ordered_commit_set_->GetCommitIdAt(proj[i]);
394 ASSERT_EQ(expected_id_order[proj[i]], projected);
395 // Since this projection is the identity, the following holds.
396 ASSERT_EQ(expected_id_order[i], projected);
397 }
398 }
399 }
400
401 Directory* directory() {
402 return dir_maker_.directory();
403 }
404
405 int64 CreateUnsyncedDirectory(const string& entry_name,
406 const string& idstring) {
407 return CreateUnsyncedDirectory(entry_name,
408 syncable::Id::CreateFromServerId(idstring));
409 }
410
411 int64 CreateUnsyncedDirectory(const string& entry_name,
412 const syncable::Id& id) {
413 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
414 MutableEntry entry(&wtrans, syncable::CREATE, wtrans.root_id(),
415 entry_name);
416 EXPECT_TRUE(entry.good());
417 entry.Put(syncable::IS_UNSYNCED, true);
418 entry.Put(syncable::IS_DIR, true);
419 entry.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
420 entry.Put(syncable::BASE_VERSION, id.ServerKnows() ? 1 : 0);
421 entry.Put(syncable::ID, id);
422 return entry.Get(META_HANDLE);
423 }
424
425 void EnableDatatype(syncable::ModelType model_type) {
426 enabled_datatypes_.Put(model_type);
427 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
428 }
429
430 void DisableDatatype(syncable::ModelType model_type) {
431 enabled_datatypes_.Remove(model_type);
432 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_);
433 }
434
435 template<typename FieldType, typename ValueType>
436 ValueType GetField(int64 metahandle, FieldType field,
437 ValueType default_value) {
438 ReadTransaction trans(FROM_HERE, directory());
439 Entry entry(&trans, GET_BY_HANDLE, metahandle);
440 EXPECT_TRUE(entry.good());
441 if (!entry.good()) {
442 return default_value;
443 }
444 EXPECT_EQ(metahandle, entry.Get(META_HANDLE));
445 return entry.Get(field);
446 }
447
448 // Helper getters that work without a transaction, to reduce boilerplate.
449 Id Get(int64 metahandle, syncable::IdField field) {
450 return GetField(metahandle, field, syncable::GetNullId());
451 }
452
453 string Get(int64 metahandle, syncable::StringField field) {
454 return GetField(metahandle, field, string());
455 }
456
457 int64 Get(int64 metahandle, syncable::Int64Field field) {
458 return GetField(metahandle, field, syncable::kInvalidMetaHandle);
459 }
460
461 int64 Get(int64 metahandle, syncable::BaseVersion field) {
462 const int64 kDefaultValue = -100;
463 return GetField(metahandle, field, kDefaultValue);
464 }
465
466 bool Get(int64 metahandle, syncable::IndexedBitField field) {
467 return GetField(metahandle, field, false);
468 }
469
470 bool Get(int64 metahandle, syncable::IsDelField field) {
471 return GetField(metahandle, field, false);
472 }
473
474 bool Get(int64 metahandle, syncable::BitField field) {
475 return GetField(metahandle, field, false);
476 }
477
478 Cryptographer* cryptographer(syncable::BaseTransaction* trans) {
479 return directory()->GetCryptographer(trans);
480 }
481
482 MessageLoop message_loop_;
483
484 // Some ids to aid tests. Only the root one's value is specific. The rest
485 // are named for test clarity.
486 // TODO(chron): Get rid of these inbuilt IDs. They only make it
487 // more confusing.
488 syncable::Id root_id_;
489 syncable::Id parent_id_;
490 syncable::Id child_id_;
491
492 TestIdFactory ids_;
493
494 TestDirectorySetterUpper dir_maker_;
495 FakeEncryptor encryptor_;
496 FakeExtensionsActivityMonitor extensions_activity_monitor_;
497 scoped_ptr<MockConnectionManager> mock_server_;
498
499 Syncer* syncer_;
500
501 scoped_ptr<SyncSession> session_;
502 scoped_ptr<SyncSessionContext> context_;
503 bool saw_syncer_event_;
504 base::TimeDelta last_short_poll_interval_received_;
505 base::TimeDelta last_long_poll_interval_received_;
506 base::TimeDelta last_sessions_commit_delay_seconds_;
507 scoped_refptr<ModelSafeWorker> worker_;
508
509 syncable::ModelTypeSet enabled_datatypes_;
510
511 DISALLOW_COPY_AND_ASSIGN(SyncerTest);
512 };
513
514 TEST_F(SyncerTest, TestCallGatherUnsyncedEntries) {
515 {
516 Syncer::UnsyncedMetaHandles handles;
517 {
518 ReadTransaction trans(FROM_HERE, directory());
519 SyncerUtil::GetUnsyncedEntries(&trans, &handles);
520 }
521 ASSERT_EQ(0u, handles.size());
522 }
523 // TODO(sync): When we can dynamically connect and disconnect the mock
524 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a
525 // regression for a very old bug.
526 }
527
528 TEST_F(SyncerTest, GetCommitIdsCommandTruncates) {
529 syncable::Id root = ids_.root();
530 // Create two server entries.
531 mock_server_->AddUpdateDirectory(ids_.MakeServer("x"), root, "X", 10, 10);
532 mock_server_->AddUpdateDirectory(ids_.MakeServer("w"), root, "W", 10, 10);
533 SyncShareAsDelegate();
534
535 // Create some new client entries.
536 CreateUnsyncedDirectory("C", ids_.MakeLocal("c"));
537 CreateUnsyncedDirectory("B", ids_.MakeLocal("b"));
538 CreateUnsyncedDirectory("D", ids_.MakeLocal("d"));
539 CreateUnsyncedDirectory("E", ids_.MakeLocal("e"));
540 CreateUnsyncedDirectory("J", ids_.MakeLocal("j"));
541
542 {
543 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
544 MutableEntry entry_x(&wtrans, GET_BY_ID, ids_.MakeServer("x"));
545 MutableEntry entry_b(&wtrans, GET_BY_ID, ids_.MakeLocal("b"));
546 MutableEntry entry_c(&wtrans, GET_BY_ID, ids_.MakeLocal("c"));
547 MutableEntry entry_d(&wtrans, GET_BY_ID, ids_.MakeLocal("d"));
548 MutableEntry entry_e(&wtrans, GET_BY_ID, ids_.MakeLocal("e"));
549 MutableEntry entry_w(&wtrans, GET_BY_ID, ids_.MakeServer("w"));
550 MutableEntry entry_j(&wtrans, GET_BY_ID, ids_.MakeLocal("j"));
551 entry_x.Put(IS_UNSYNCED, true);
552 entry_b.Put(PARENT_ID, entry_x.Get(ID));
553 entry_d.Put(PARENT_ID, entry_b.Get(ID));
554 entry_c.Put(PARENT_ID, entry_x.Get(ID));
555 entry_c.PutPredecessor(entry_b.Get(ID));
556 entry_e.Put(PARENT_ID, entry_c.Get(ID));
557 entry_w.PutPredecessor(entry_x.Get(ID));
558 entry_w.Put(IS_UNSYNCED, true);
559 entry_w.Put(SERVER_VERSION, 20);
560 entry_w.Put(IS_UNAPPLIED_UPDATE, true); // Fake a conflict.
561 entry_j.PutPredecessor(entry_w.Get(ID));
562 }
563
564 // The arrangement is now: x (b (d) c (e)) w j
565 // Entry "w" is in conflict, making its sucessors unready to commit.
566 vector<int64> unsynced_handle_view;
567 vector<syncable::Id> expected_order;
568 {
569 ReadTransaction rtrans(FROM_HERE, directory());
570 SyncerUtil::GetUnsyncedEntries(&rtrans, &unsynced_handle_view);
571 }
572 // The expected order is "x", "b", "c", "d", "e", truncated appropriately.
573 expected_order.push_back(ids_.MakeServer("x"));
574 expected_order.push_back(ids_.MakeLocal("b"));
575 expected_order.push_back(ids_.MakeLocal("c"));
576 expected_order.push_back(ids_.MakeLocal("d"));
577 expected_order.push_back(ids_.MakeLocal("e"));
578 DoTruncationTest(unsynced_handle_view, expected_order);
579 }
580
581 TEST_F(SyncerTest, GetCommitIdsFiltersThrottledEntries) {
582 const syncable::ModelTypeSet throttled_types(syncable::BOOKMARKS);
583 sync_pb::EntitySpecifics bookmark_data;
584 AddDefaultFieldValue(syncable::BOOKMARKS, &bookmark_data);
585
586 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10);
587 SyncShareAsDelegate();
588
589 {
590 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
591 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
592 ASSERT_TRUE(A.good());
593 A.Put(IS_UNSYNCED, true);
594 A.Put(SPECIFICS, bookmark_data);
595 A.Put(NON_UNIQUE_NAME, "bookmark");
596 }
597
598 // Now set the throttled types.
599 context_->SetUnthrottleTime(
600 throttled_types,
601 base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1200));
602 SyncShareAsDelegate();
603
604 {
605 // Nothing should have been committed as bookmarks is throttled.
606 ReadTransaction rtrans(FROM_HERE, directory());
607 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
608 ASSERT_TRUE(entryA.good());
609 EXPECT_TRUE(entryA.Get(IS_UNSYNCED));
610 }
611
612 // Now unthrottle.
613 context_->SetUnthrottleTime(
614 throttled_types,
615 base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1200));
616 SyncShareAsDelegate();
617 {
618 // It should have been committed.
619 ReadTransaction rtrans(FROM_HERE, directory());
620 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
621 ASSERT_TRUE(entryA.good());
622 EXPECT_FALSE(entryA.Get(IS_UNSYNCED));
623 }
624 }
625
626 // We use a macro so we can preserve the error location.
627 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \
628 parent_id, version, server_version, id_fac, rtrans) \
629 do { \
630 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \
631 ASSERT_TRUE(entryA.good()); \
632 /* We don't use EXPECT_EQ here because when the left side param is false,
633 gcc 4.6 warns about converting 'false' to pointer type for argument 1. */ \
634 EXPECT_TRUE(is_unsynced == entryA.Get(IS_UNSYNCED)); \
635 EXPECT_TRUE(is_unapplied == entryA.Get(IS_UNAPPLIED_UPDATE)); \
636 EXPECT_TRUE(prev_initialized == \
637 syncable::IsRealDataType(syncable::GetModelTypeFromSpecifics( \
638 entryA.Get(BASE_SERVER_SPECIFICS)))); \
639 EXPECT_TRUE(parent_id == -1 || \
640 entryA.Get(PARENT_ID) == id_fac.FromNumber(parent_id)); \
641 EXPECT_EQ(version, entryA.Get(BASE_VERSION)); \
642 EXPECT_EQ(server_version, entryA.Get(SERVER_VERSION)); \
643 } while (0)
644
645 TEST_F(SyncerTest, GetCommitIdsFiltersUnreadyEntries) {
646 KeyParams key_params = {"localhost", "dummy", "foobar"};
647 KeyParams other_params = {"localhost", "dummy", "foobar2"};
648 sync_pb::EntitySpecifics bookmark, encrypted_bookmark;
649 bookmark.mutable_bookmark()->set_url("url");
650 bookmark.mutable_bookmark()->set_title("title");
651 AddDefaultFieldValue(syncable::BOOKMARKS, &encrypted_bookmark);
652 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10);
653 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10);
654 mock_server_->AddUpdateDirectory(3, 0, "C", 10, 10);
655 mock_server_->AddUpdateDirectory(4, 0, "D", 10, 10);
656 SyncShareAsDelegate();
657 // Server side change will put A in conflict.
658 mock_server_->AddUpdateDirectory(1, 0, "A", 20, 20);
659 {
660 // Mark bookmarks as encrypted and set the cryptographer to have pending
661 // keys.
662 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
663 browser_sync::Cryptographer other_cryptographer(&encryptor_);
664 other_cryptographer.AddKey(other_params);
665 sync_pb::EntitySpecifics specifics;
666 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
667 other_cryptographer.GetKeys(nigori->mutable_encrypted());
668 nigori->set_encrypt_bookmarks(true);
669 // Set up with an old passphrase, but have pending keys
670 cryptographer(&wtrans)->AddKey(key_params);
671 cryptographer(&wtrans)->Encrypt(bookmark,
672 encrypted_bookmark.mutable_encrypted());
673 cryptographer(&wtrans)->Update(*nigori);
674
675 // In conflict but properly encrypted.
676 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
677 ASSERT_TRUE(A.good());
678 A.Put(IS_UNSYNCED, true);
679 A.Put(SPECIFICS, encrypted_bookmark);
680 A.Put(NON_UNIQUE_NAME, kEncryptedString);
681 // Not in conflict and properly encrypted.
682 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
683 ASSERT_TRUE(B.good());
684 B.Put(IS_UNSYNCED, true);
685 B.Put(SPECIFICS, encrypted_bookmark);
686 B.Put(NON_UNIQUE_NAME, kEncryptedString);
687 // Unencrypted specifics.
688 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
689 ASSERT_TRUE(C.good());
690 C.Put(IS_UNSYNCED, true);
691 C.Put(NON_UNIQUE_NAME, kEncryptedString);
692 // Unencrypted non_unique_name.
693 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
694 ASSERT_TRUE(D.good());
695 D.Put(IS_UNSYNCED, true);
696 D.Put(SPECIFICS, encrypted_bookmark);
697 D.Put(NON_UNIQUE_NAME, "not encrypted");
698 }
699 SyncShareAsDelegate();
700 {
701 // We remove any unready entries from the status controller's unsynced
702 // handles, so this should remain 0 even though the entries didn't commit.
703 EXPECT_EQ(0U, session_->status_controller().unsynced_handles().size());
704 // Nothing should have commited due to bookmarks being encrypted and
705 // the cryptographer having pending keys. A would have been resolved
706 // as a simple conflict, but still be unsynced until the next sync cycle.
707 ReadTransaction rtrans(FROM_HERE, directory());
708 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_, &rtrans);
709 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_, &rtrans);
710 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
711 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
712
713 // Resolve the pending keys.
714 cryptographer(&rtrans)->DecryptPendingKeys(other_params);
715 }
716 SyncShareAsDelegate();
717 {
718 // 2 unsynced handles to reflect the items that committed succesfully.
719 EXPECT_EQ(2U, session_->status_controller().unsynced_handles().size());
720 // All properly encrypted and non-conflicting items should commit. "A" was
721 // conflicting, but last sync cycle resolved it as simple conflict, so on
722 // this sync cycle it committed succesfullly.
723 ReadTransaction rtrans(FROM_HERE, directory());
724 // Committed successfully.
725 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
726 // Committed successfully.
727 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
728 // Was not properly encrypted.
729 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans);
730 // Was not properly encrypted.
731 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans);
732 }
733 {
734 // Fix the remaining items.
735 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
736 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
737 ASSERT_TRUE(C.good());
738 C.Put(SPECIFICS, encrypted_bookmark);
739 C.Put(NON_UNIQUE_NAME, kEncryptedString);
740 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
741 ASSERT_TRUE(D.good());
742 D.Put(SPECIFICS, encrypted_bookmark);
743 D.Put(NON_UNIQUE_NAME, kEncryptedString);
744 }
745 SyncShareAsDelegate();
746 {
747 // We attempted to commit two items.
748 EXPECT_EQ(2U, session_->status_controller().unsynced_handles().size());
749 EXPECT_TRUE(session_->status_controller().did_commit_items());
750 // None should be unsynced anymore.
751 ReadTransaction rtrans(FROM_HERE, directory());
752 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans);
753 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans);
754 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_, &rtrans);
755 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_, &rtrans);
756 }
757 }
758
759 TEST_F(SyncerTest, EncryptionAwareConflicts) {
760 KeyParams key_params = {"localhost", "dummy", "foobar"};
761 browser_sync::Cryptographer other_cryptographer(&encryptor_);
762 other_cryptographer.AddKey(key_params);
763 sync_pb::EntitySpecifics bookmark, encrypted_bookmark, modified_bookmark;
764 bookmark.mutable_bookmark()->set_title("title");
765 other_cryptographer.Encrypt(bookmark,
766 encrypted_bookmark.mutable_encrypted());
767 AddDefaultFieldValue(syncable::BOOKMARKS, &encrypted_bookmark);
768 modified_bookmark.mutable_bookmark()->set_title("title2");
769 other_cryptographer.Encrypt(modified_bookmark,
770 modified_bookmark.mutable_encrypted());
771 sync_pb::EntitySpecifics pref, encrypted_pref, modified_pref;
772 pref.mutable_preference()->set_name("name");
773 AddDefaultFieldValue(syncable::PREFERENCES, &encrypted_pref);
774 other_cryptographer.Encrypt(pref,
775 encrypted_pref.mutable_encrypted());
776 modified_pref.mutable_preference()->set_name("name2");
777 other_cryptographer.Encrypt(modified_pref,
778 modified_pref.mutable_encrypted());
779 {
780 // Mark bookmarks and preferences as encrypted and set the cryptographer to
781 // have pending keys.
782 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
783 sync_pb::EntitySpecifics specifics;
784 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
785 other_cryptographer.GetKeys(nigori->mutable_encrypted());
786 nigori->set_encrypt_bookmarks(true);
787 nigori->set_encrypt_preferences(true);
788 cryptographer(&wtrans)->Update(*nigori);
789 EXPECT_TRUE(cryptographer(&wtrans)->has_pending_keys());
790 }
791
792 mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark);
793 mock_server_->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark);
794 mock_server_->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark);
795 mock_server_->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref);
796 SyncShareAsDelegate();
797 {
798 EXPECT_EQ(0U, session_->status_controller().unsynced_handles().size());
799 // Initial state. Everything is normal.
800 ReadTransaction rtrans(FROM_HERE, directory());
801 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_, &rtrans);
802 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_, &rtrans);
803 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_, &rtrans);
804 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_, &rtrans);
805 }
806
807 // Server side encryption will not be applied due to undecryptable data.
808 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items.
809 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 20, 20, true, 0,
810 encrypted_bookmark);
811 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 20, 20, false, 2,
812 encrypted_bookmark);
813 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 20, 20, false, 1,
814 encrypted_bookmark);
815 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 20, 20, false, 0,
816 encrypted_pref);
817 SyncShareAsDelegate();
818 {
819 EXPECT_EQ(0U, session_->status_controller().unsynced_handles().size());
820 // All should be unapplied due to being undecryptable and have a valid
821 // BASE_SERVER_SPECIFICS.
822 ReadTransaction rtrans(FROM_HERE, directory());
823 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_, &rtrans);
824 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_, &rtrans);
825 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
826 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_, &rtrans);
827 }
828
829 // Server side change that don't modify anything should not affect
830 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes).
831 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 30, 30, true, 0,
832 encrypted_bookmark);
833 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 30, 30, false, 2,
834 encrypted_bookmark);
835 // Item 3 doesn't change.
836 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 30, 30, false, 0,
837 encrypted_pref);
838 SyncShareAsDelegate();
839 {
840 EXPECT_EQ(0U, session_->status_controller().unsynced_handles().size());
841 // Items 1, 2, and 4 should have newer server versions, 3 remains the same.
842 // All should remain unapplied due to be undecryptable.
843 ReadTransaction rtrans(FROM_HERE, directory());
844 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_, &rtrans);
845 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
846 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans);
847 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
848 }
849
850 // Positional changes, parent changes, and specifics changes should reset
851 // BASE_SERVER_SPECIFICS.
852 // Became unencrypted.
853 mock_server_->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark);
854 // Reordered to after item 2.
855 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 30, 30, false, 3,
856 encrypted_bookmark);
857 SyncShareAsDelegate();
858 {
859 EXPECT_EQ(0U, session_->status_controller().unsynced_handles().size());
860 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set.
861 // Items 1 is now unencrypted, so should have applied normally.
862 ReadTransaction rtrans(FROM_HERE, directory());
863 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_, &rtrans);
864 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans);
865 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_, &rtrans);
866 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans);
867 }
868
869 // Make local changes, which should remain unsynced for items 2, 3, 4.
870 {
871 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
872 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
873 ASSERT_TRUE(A.good());
874 A.Put(SPECIFICS, modified_bookmark);
875 A.Put(NON_UNIQUE_NAME, kEncryptedString);
876 A.Put(IS_UNSYNCED, true);
877 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
878 ASSERT_TRUE(B.good());
879 B.Put(SPECIFICS, modified_bookmark);
880 B.Put(NON_UNIQUE_NAME, kEncryptedString);
881 B.Put(IS_UNSYNCED, true);
882 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3));
883 ASSERT_TRUE(C.good());
884 C.Put(SPECIFICS, modified_bookmark);
885 C.Put(NON_UNIQUE_NAME, kEncryptedString);
886 C.Put(IS_UNSYNCED, true);
887 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4));
888 ASSERT_TRUE(D.good());
889 D.Put(SPECIFICS, modified_pref);
890 D.Put(NON_UNIQUE_NAME, kEncryptedString);
891 D.Put(IS_UNSYNCED, true);
892 }
893 SyncShareAsDelegate();
894 {
895 EXPECT_EQ(0U, session_->status_controller().unsynced_handles().size());
896 // Item 1 remains unsynced due to there being pending keys.
897 // Items 2, 3, 4 should remain unsynced since they were not up to date.
898 ReadTransaction rtrans(FROM_HERE, directory());
899 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_, &rtrans);
900 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_, &rtrans);
901 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_, &rtrans);
902 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_, &rtrans);
903 }
904
905 {
906 ReadTransaction rtrans(FROM_HERE, directory());
907 // Resolve the pending keys.
908 cryptographer(&rtrans)->DecryptPendingKeys(key_params);
909 }
910 // First cycle resolves conflicts, second cycle commits changes.
911 SyncShareAsDelegate();
912 EXPECT_EQ(2, session_->status_controller().syncer_status().
913 num_server_overwrites);
914 EXPECT_EQ(1, session_->status_controller().syncer_status().
915 num_local_overwrites);
916 // We attempted to commit item 1.
917 EXPECT_EQ(1U, session_->status_controller().unsynced_handles().size());
918 EXPECT_TRUE(session_->status_controller().did_commit_items());
919 SyncShareAsDelegate();
920 {
921 // Everything should be resolved now. The local changes should have
922 // overwritten the server changes for 2 and 4, while the server changes
923 // overwrote the local for entry 3.
924 // We attempted to commit two handles.
925 EXPECT_EQ(0, session_->status_controller().syncer_status().
926 num_server_overwrites);
927 EXPECT_EQ(0, session_->status_controller().syncer_status().
928 num_local_overwrites);
929 EXPECT_EQ(2U, session_->status_controller().unsynced_handles().size());
930 EXPECT_TRUE(session_->status_controller().did_commit_items());
931 ReadTransaction rtrans(FROM_HERE, directory());
932 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_, &rtrans);
933 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_, &rtrans);
934 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_, &rtrans);
935 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_, &rtrans);
936 }
937 }
938
939 #undef VERIFY_ENTRY
940
941 TEST_F(SyncerTest, NigoriConflicts) {
942 KeyParams local_key_params = {"localhost", "dummy", "blargle"};
943 KeyParams other_key_params = {"localhost", "dummy", "foobar"};
944 browser_sync::Cryptographer other_cryptographer(&encryptor_);
945 other_cryptographer.AddKey(other_key_params);
946 syncable::ModelTypeSet encrypted_types(syncable::PASSWORDS, syncable::NIGORI);
947 sync_pb::EntitySpecifics initial_nigori_specifics;
948 initial_nigori_specifics.mutable_nigori();
949 mock_server_->SetNigori(1, 10, 10, initial_nigori_specifics);
950
951 // Data for testing encryption/decryption.
952 sync_pb::EntitySpecifics other_encrypted_specifics;
953 other_encrypted_specifics.mutable_bookmark()->set_title("title");
954 other_cryptographer.Encrypt(
955 other_encrypted_specifics,
956 other_encrypted_specifics.mutable_encrypted());
957 sync_pb::EntitySpecifics our_encrypted_specifics;
958 our_encrypted_specifics.mutable_bookmark()->set_title("title2");
959
960 // Receive the initial nigori node.
961 SyncShareAsDelegate();
962 encrypted_types = syncable::ModelTypeSet::All();
963 {
964 // Local changes with different passphrase, different types, and sync_tabs.
965 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
966 sync_pb::EntitySpecifics specifics;
967 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
968 cryptographer(&wtrans)->AddKey(local_key_params);
969 cryptographer(&wtrans)->Encrypt(
970 our_encrypted_specifics,
971 our_encrypted_specifics.mutable_encrypted());
972 cryptographer(&wtrans)->GetKeys(
973 nigori->mutable_encrypted());
974 cryptographer(&wtrans)->UpdateNigoriFromEncryptedTypes(nigori);
975 nigori->set_sync_tabs(true);
976 cryptographer(&wtrans)->set_encrypt_everything();
977 MutableEntry nigori_entry(&wtrans, GET_BY_SERVER_TAG,
978 syncable::ModelTypeToRootTag(syncable::NIGORI));
979 ASSERT_TRUE(nigori_entry.good());
980 nigori_entry.Put(SPECIFICS, specifics);
981 nigori_entry.Put(IS_UNSYNCED, true);
982 EXPECT_FALSE(cryptographer(&wtrans)->has_pending_keys());
983 EXPECT_TRUE(encrypted_types.Equals(
984 cryptographer(&wtrans)->GetEncryptedTypes()));
985 }
986 {
987 sync_pb::EntitySpecifics specifics;
988 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
989 other_cryptographer.GetKeys(nigori->mutable_encrypted());
990 nigori->set_encrypt_bookmarks(true);
991 nigori->set_encrypt_preferences(true);
992 nigori->set_encrypt_everything(false);
993 mock_server_->SetNigori(1, 20, 20, specifics);
994 }
995
996 // Will result in downloading the server nigori, which puts the local nigori
997 // in a state of conflict. This is resolved by merging the local and server
998 // data (with priority given to the server's encryption keys if they are
999 // undecryptable), which we then commit. The cryptographer should have pending
1000 // keys and merge the set of encrypted types.
1001 SyncShareAsDelegate(); // Resolve conflict in this cycle.
1002 SyncShareAsDelegate(); // Commit local change in this cycle.
1003 {
1004 // Ensure the nigori data merged (encrypted types, sync_tabs).
1005 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1006 MutableEntry nigori_entry(&wtrans, GET_BY_SERVER_TAG,
1007 syncable::ModelTypeToRootTag(syncable::NIGORI));
1008 ASSERT_TRUE(nigori_entry.good());
1009 EXPECT_FALSE(nigori_entry.Get(IS_UNAPPLIED_UPDATE));
1010 EXPECT_FALSE(nigori_entry.Get(IS_UNSYNCED));
1011 sync_pb::EntitySpecifics specifics = nigori_entry.Get(SPECIFICS);
1012 EXPECT_TRUE(cryptographer(&wtrans)->has_pending_keys());
1013 EXPECT_TRUE(encrypted_types.Equals(
1014 cryptographer(&wtrans)->GetEncryptedTypes()));
1015 EXPECT_TRUE(cryptographer(&wtrans)->encrypt_everything());
1016 EXPECT_TRUE(specifics.nigori().sync_tabs());
1017 // Supply the pending keys. Afterwards, we should be able to decrypt both
1018 // our own encrypted data and data encrypted by the other cryptographer,
1019 // but the key provided by the other cryptographer should be the default.
1020 EXPECT_TRUE(cryptographer(&wtrans)->DecryptPendingKeys(other_key_params));
1021 EXPECT_FALSE(cryptographer(&wtrans)->has_pending_keys());
1022 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
1023 cryptographer(&wtrans)->GetKeys(nigori->mutable_encrypted());
1024 cryptographer(&wtrans)->UpdateNigoriFromEncryptedTypes(nigori);
1025 // Normally this would be written as part of SetPassphrase, but we do it
1026 // manually for the test.
1027 nigori_entry.Put(SPECIFICS, specifics);
1028 nigori_entry.Put(IS_UNSYNCED, true);
1029 }
1030
1031 SyncShareAsDelegate();
1032 {
1033 // Ensure everything is committed and stable now. The cryptographer
1034 // should be able to decrypt both sets of keys, sync_tabs should be true,
1035 // and the encrypted types should have been unioned.
1036 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1037 MutableEntry nigori_entry(&wtrans, GET_BY_SERVER_TAG,
1038 syncable::ModelTypeToRootTag(syncable::NIGORI));
1039 ASSERT_TRUE(nigori_entry.good());
1040 EXPECT_FALSE(nigori_entry.Get(IS_UNAPPLIED_UPDATE));
1041 EXPECT_FALSE(nigori_entry.Get(IS_UNSYNCED));
1042 EXPECT_TRUE(cryptographer(&wtrans)->CanDecrypt(
1043 our_encrypted_specifics.encrypted()));
1044 EXPECT_FALSE(cryptographer(&wtrans)->
1045 CanDecryptUsingDefaultKey(our_encrypted_specifics.encrypted()));
1046 EXPECT_TRUE(cryptographer(&wtrans)->CanDecrypt(
1047 other_encrypted_specifics.encrypted()));
1048 EXPECT_TRUE(cryptographer(&wtrans)->
1049 CanDecryptUsingDefaultKey(other_encrypted_specifics.encrypted()));
1050 EXPECT_TRUE(nigori_entry.Get(SPECIFICS).nigori().sync_tabs());
1051 }
1052 }
1053
1054 TEST_F(SyncerTest, TestGetUnsyncedAndSimpleCommit) {
1055 {
1056 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1057 MutableEntry parent(&wtrans, syncable::CREATE, wtrans.root_id(),
1058 "Pete");
1059 ASSERT_TRUE(parent.good());
1060 parent.Put(syncable::IS_UNSYNCED, true);
1061 parent.Put(syncable::IS_DIR, true);
1062 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1063 parent.Put(syncable::BASE_VERSION, 1);
1064 parent.Put(syncable::ID, parent_id_);
1065 MutableEntry child(&wtrans, syncable::CREATE, parent_id_, "Pete");
1066 ASSERT_TRUE(child.good());
1067 child.Put(syncable::ID, child_id_);
1068 child.Put(syncable::BASE_VERSION, 1);
1069 WriteTestDataToEntry(&wtrans, &child);
1070 }
1071
1072 const StatusController& status = session_->status_controller();
1073 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1074 EXPECT_EQ(2u, status.unsynced_handles().size());
1075 ASSERT_EQ(2u, mock_server_->committed_ids().size());
1076 // If this test starts failing, be aware other sort orders could be valid.
1077 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1078 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1079 {
1080 ReadTransaction rt(FROM_HERE, directory());
1081 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
1082 ASSERT_TRUE(entry.good());
1083 VerifyTestDataInEntry(&rt, &entry);
1084 }
1085 }
1086
1087 TEST_F(SyncerTest, TestPurgeWhileUnsynced) {
1088 // Similar to above, but throw a purge operation into the mix. Bug 49278.
1089 syncable::Id pref_node_id = TestIdFactory::MakeServer("Tim");
1090 {
1091 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1092 MutableEntry parent(&wtrans, syncable::CREATE, wtrans.root_id(), "Pete");
1093 ASSERT_TRUE(parent.good());
1094 parent.Put(syncable::IS_UNSYNCED, true);
1095 parent.Put(syncable::IS_DIR, true);
1096 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1097 parent.Put(syncable::BASE_VERSION, 1);
1098 parent.Put(syncable::ID, parent_id_);
1099 MutableEntry child(&wtrans, syncable::CREATE, parent_id_, "Pete");
1100 ASSERT_TRUE(child.good());
1101 child.Put(syncable::ID, child_id_);
1102 child.Put(syncable::BASE_VERSION, 1);
1103 WriteTestDataToEntry(&wtrans, &child);
1104
1105 MutableEntry parent2(&wtrans, syncable::CREATE, wtrans.root_id(), "Tim");
1106 ASSERT_TRUE(parent2.good());
1107 parent2.Put(syncable::IS_UNSYNCED, true);
1108 parent2.Put(syncable::IS_DIR, true);
1109 parent2.Put(syncable::SPECIFICS, DefaultPreferencesSpecifics());
1110 parent2.Put(syncable::BASE_VERSION, 1);
1111 parent2.Put(syncable::ID, pref_node_id);
1112 }
1113
1114 directory()->PurgeEntriesWithTypeIn(
1115 syncable::ModelTypeSet(syncable::PREFERENCES));
1116
1117 const StatusController& status = session_->status_controller();
1118 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1119 EXPECT_EQ(2U, status.unsynced_handles().size());
1120 ASSERT_EQ(2U, mock_server_->committed_ids().size());
1121 // If this test starts failing, be aware other sort orders could be valid.
1122 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1123 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1124 {
1125 ReadTransaction rt(FROM_HERE, directory());
1126 Entry entry(&rt, syncable::GET_BY_ID, child_id_);
1127 ASSERT_TRUE(entry.good());
1128 VerifyTestDataInEntry(&rt, &entry);
1129 }
1130 directory()->SaveChanges();
1131 {
1132 ReadTransaction rt(FROM_HERE, directory());
1133 Entry entry(&rt, syncable::GET_BY_ID, pref_node_id);
1134 ASSERT_FALSE(entry.good());
1135 }
1136 }
1137
1138 TEST_F(SyncerTest, TestPurgeWhileUnapplied) {
1139 // Similar to above, but for unapplied items. Bug 49278.
1140 {
1141 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1142 MutableEntry parent(&wtrans, syncable::CREATE, wtrans.root_id(), "Pete");
1143 ASSERT_TRUE(parent.good());
1144 parent.Put(syncable::IS_UNAPPLIED_UPDATE, true);
1145 parent.Put(syncable::IS_DIR, true);
1146 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1147 parent.Put(syncable::BASE_VERSION, 1);
1148 parent.Put(syncable::ID, parent_id_);
1149 }
1150
1151 directory()->PurgeEntriesWithTypeIn(
1152 syncable::ModelTypeSet(syncable::BOOKMARKS));
1153
1154 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1155 directory()->SaveChanges();
1156 {
1157 ReadTransaction rt(FROM_HERE, directory());
1158 Entry entry(&rt, syncable::GET_BY_ID, parent_id_);
1159 ASSERT_FALSE(entry.good());
1160 }
1161 }
1162
1163 TEST_F(SyncerTest, TestCommitListOrderingTwoItemsTall) {
1164 CommitOrderingTest items[] = {
1165 {1, ids_.FromNumber(-1001), ids_.FromNumber(-1000)},
1166 {0, ids_.FromNumber(-1000), ids_.FromNumber(0)},
1167 CommitOrderingTest::LAST_COMMIT_ITEM,
1168 };
1169 RunCommitOrderingTest(items);
1170 }
1171
1172 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTall) {
1173 CommitOrderingTest items[] = {
1174 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1175 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1176 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1177 CommitOrderingTest::LAST_COMMIT_ITEM,
1178 };
1179 RunCommitOrderingTest(items);
1180 }
1181
1182 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTallLimitedSize) {
1183 context_->set_max_commit_batch_size(2);
1184 CommitOrderingTest items[] = {
1185 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)},
1186 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)},
1187 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)},
1188 CommitOrderingTest::LAST_COMMIT_ITEM,
1189 };
1190 RunCommitOrderingTest(items);
1191 }
1192
1193 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItem) {
1194 CommitOrderingTest items[] = {
1195 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1196 CommitOrderingTest::LAST_COMMIT_ITEM,
1197 };
1198 RunCommitOrderingTest(items);
1199 }
1200
1201 TEST_F(SyncerTest, TestCommitListOrderingSingleUncommittedDeletedItem) {
1202 CommitOrderingTest items[] = {
1203 {-1, ids_.FromNumber(-1000), ids_.FromNumber(0), {DELETED}},
1204 CommitOrderingTest::LAST_COMMIT_ITEM,
1205 };
1206 RunCommitOrderingTest(items);
1207 }
1208
1209 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItemWithUnroll) {
1210 CommitOrderingTest items[] = {
1211 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1212 CommitOrderingTest::LAST_COMMIT_ITEM,
1213 };
1214 RunCommitOrderingTest(items);
1215 }
1216
1217 TEST_F(SyncerTest,
1218 TestCommitListOrderingSingleLongDeletedItemWithUnroll) {
1219 CommitOrderingTest items[] = {
1220 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1221 CommitOrderingTest::LAST_COMMIT_ITEM,
1222 };
1223 RunCommitOrderingTest(items);
1224 }
1225
1226 TEST_F(SyncerTest, TestCommitListOrderingTwoLongDeletedItemWithUnroll) {
1227 CommitOrderingTest items[] = {
1228 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1229 {-1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}},
1230 CommitOrderingTest::LAST_COMMIT_ITEM,
1231 };
1232 RunCommitOrderingTest(items);
1233 }
1234
1235 TEST_F(SyncerTest, TestCommitListOrdering3LongDeletedItemsWithSizeLimit) {
1236 context_->set_max_commit_batch_size(2);
1237 CommitOrderingTest items[] = {
1238 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1239 {1, ids_.FromNumber(1001), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1240 {2, ids_.FromNumber(1002), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1241 CommitOrderingTest::LAST_COMMIT_ITEM,
1242 };
1243 RunCommitOrderingTest(items);
1244 }
1245
1246 TEST_F(SyncerTest, TestCommitListOrderingTwoDeletedItemsWithUnroll) {
1247 CommitOrderingTest items[] = {
1248 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}},
1249 {-1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED}},
1250 CommitOrderingTest::LAST_COMMIT_ITEM,
1251 };
1252 RunCommitOrderingTest(items);
1253 }
1254
1255 TEST_F(SyncerTest, TestCommitListOrderingComplexDeletionScenario) {
1256 CommitOrderingTest items[] = {
1257 { 0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1258 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1259 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1260 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1261 {2, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1262 CommitOrderingTest::LAST_COMMIT_ITEM,
1263 };
1264 RunCommitOrderingTest(items);
1265 }
1266
1267 TEST_F(SyncerTest,
1268 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes) {
1269 CommitOrderingTest items[] = {
1270 { 0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1271 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}},
1272 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}},
1273 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}},
1274 {2, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}},
1275 {3, ids_.FromNumber(1005), ids_.FromNumber(1003), {DELETED}},
1276 CommitOrderingTest::LAST_COMMIT_ITEM,
1277 };
1278 RunCommitOrderingTest(items);
1279 }
1280
1281 TEST_F(SyncerTest, TestCommitListOrderingDeleteMovedItems) {
1282 CommitOrderingTest items[] = {
1283 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}},
1284 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME,
1285 MOVED_FROM_ROOT}},
1286 CommitOrderingTest::LAST_COMMIT_ITEM,
1287 };
1288 RunCommitOrderingTest(items);
1289 }
1290
1291 TEST_F(SyncerTest, TestCommitListOrderingWithNesting) {
1292 const base::Time& now_minus_2h =
1293 base::Time::Now() - base::TimeDelta::FromHours(2);
1294 {
1295 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1296 {
1297 MutableEntry parent(&wtrans, syncable::CREATE, wtrans.root_id(),
1298 "Bob");
1299 ASSERT_TRUE(parent.good());
1300 parent.Put(syncable::IS_UNSYNCED, true);
1301 parent.Put(syncable::IS_DIR, true);
1302 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1303 parent.Put(syncable::ID, ids_.FromNumber(100));
1304 parent.Put(syncable::BASE_VERSION, 1);
1305 MutableEntry child(&wtrans, syncable::CREATE, ids_.FromNumber(100),
1306 "Bob");
1307 ASSERT_TRUE(child.good());
1308 child.Put(syncable::IS_UNSYNCED, true);
1309 child.Put(syncable::IS_DIR, true);
1310 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1311 child.Put(syncable::ID, ids_.FromNumber(101));
1312 child.Put(syncable::BASE_VERSION, 1);
1313 MutableEntry grandchild(&wtrans, syncable::CREATE, ids_.FromNumber(101),
1314 "Bob");
1315 ASSERT_TRUE(grandchild.good());
1316 grandchild.Put(syncable::ID, ids_.FromNumber(102));
1317 grandchild.Put(syncable::IS_UNSYNCED, true);
1318 grandchild.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1319 grandchild.Put(syncable::BASE_VERSION, 1);
1320 }
1321 {
1322 // Create three deleted items which deletions we expect to be sent to the
1323 // server.
1324 MutableEntry parent(&wtrans, syncable::CREATE, wtrans.root_id(),
1325 "Pete");
1326 ASSERT_TRUE(parent.good());
1327 parent.Put(syncable::IS_UNSYNCED, true);
1328 parent.Put(syncable::IS_DIR, true);
1329 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1330 parent.Put(syncable::IS_DEL, true);
1331 parent.Put(syncable::ID, ids_.FromNumber(103));
1332 parent.Put(syncable::BASE_VERSION, 1);
1333 parent.Put(syncable::MTIME, now_minus_2h);
1334 MutableEntry child(&wtrans, syncable::CREATE, ids_.FromNumber(103),
1335 "Pete");
1336 ASSERT_TRUE(child.good());
1337 child.Put(syncable::IS_UNSYNCED, true);
1338 child.Put(syncable::IS_DIR, true);
1339 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1340 child.Put(syncable::IS_DEL, true);
1341 child.Put(syncable::ID, ids_.FromNumber(104));
1342 child.Put(syncable::BASE_VERSION, 1);
1343 child.Put(syncable::MTIME, now_minus_2h);
1344 MutableEntry grandchild(&wtrans, syncable::CREATE, ids_.FromNumber(104),
1345 "Pete");
1346 ASSERT_TRUE(grandchild.good());
1347 grandchild.Put(syncable::IS_UNSYNCED, true);
1348 grandchild.Put(syncable::ID, ids_.FromNumber(105));
1349 grandchild.Put(syncable::IS_DEL, true);
1350 grandchild.Put(syncable::IS_DIR, false);
1351 grandchild.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1352 grandchild.Put(syncable::BASE_VERSION, 1);
1353 grandchild.Put(syncable::MTIME, now_minus_2h);
1354 }
1355 }
1356
1357 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1358 EXPECT_EQ(6u, session_->status_controller().unsynced_handles().size());
1359 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1360 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set.
1361 // It will treat these like moves.
1362 vector<syncable::Id> commit_ids(mock_server_->committed_ids());
1363 EXPECT_TRUE(ids_.FromNumber(100) == commit_ids[0]);
1364 EXPECT_TRUE(ids_.FromNumber(101) == commit_ids[1]);
1365 EXPECT_TRUE(ids_.FromNumber(102) == commit_ids[2]);
1366 // We don't guarantee the delete orders in this test, only that they occur
1367 // at the end.
1368 std::sort(commit_ids.begin() + 3, commit_ids.end());
1369 EXPECT_TRUE(ids_.FromNumber(103) == commit_ids[3]);
1370 EXPECT_TRUE(ids_.FromNumber(104) == commit_ids[4]);
1371 EXPECT_TRUE(ids_.FromNumber(105) == commit_ids[5]);
1372 }
1373
1374 TEST_F(SyncerTest, TestCommitListOrderingWithNewItems) {
1375 {
1376 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1377 MutableEntry parent(&wtrans, syncable::CREATE, wtrans.root_id(), "1");
1378 ASSERT_TRUE(parent.good());
1379 parent.Put(syncable::IS_UNSYNCED, true);
1380 parent.Put(syncable::IS_DIR, true);
1381 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1382 parent.Put(syncable::ID, parent_id_);
1383 MutableEntry child(&wtrans, syncable::CREATE, wtrans.root_id(), "2");
1384 ASSERT_TRUE(child.good());
1385 child.Put(syncable::IS_UNSYNCED, true);
1386 child.Put(syncable::IS_DIR, true);
1387 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1388 child.Put(syncable::ID, child_id_);
1389 parent.Put(syncable::BASE_VERSION, 1);
1390 child.Put(syncable::BASE_VERSION, 1);
1391 }
1392 {
1393 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1394 MutableEntry parent(&wtrans, syncable::CREATE, parent_id_, "A");
1395 ASSERT_TRUE(parent.good());
1396 parent.Put(syncable::IS_UNSYNCED, true);
1397 parent.Put(syncable::IS_DIR, true);
1398 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1399 parent.Put(syncable::ID, ids_.FromNumber(102));
1400 MutableEntry child(&wtrans, syncable::CREATE, parent_id_, "B");
1401 ASSERT_TRUE(child.good());
1402 child.Put(syncable::IS_UNSYNCED, true);
1403 child.Put(syncable::IS_DIR, true);
1404 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1405 child.Put(syncable::ID, ids_.FromNumber(-103));
1406 parent.Put(syncable::BASE_VERSION, 1);
1407 }
1408 {
1409 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1410 MutableEntry parent(&wtrans, syncable::CREATE, child_id_, "A");
1411 ASSERT_TRUE(parent.good());
1412 parent.Put(syncable::IS_UNSYNCED, true);
1413 parent.Put(syncable::IS_DIR, true);
1414 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1415 parent.Put(syncable::ID, ids_.FromNumber(-104));
1416 MutableEntry child(&wtrans, syncable::CREATE, child_id_, "B");
1417 ASSERT_TRUE(child.good());
1418 child.Put(syncable::IS_UNSYNCED, true);
1419 child.Put(syncable::IS_DIR, true);
1420 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1421 child.Put(syncable::ID, ids_.FromNumber(105));
1422 child.Put(syncable::BASE_VERSION, 1);
1423 }
1424
1425 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1426 EXPECT_EQ(6u, session_->status_controller().unsynced_handles().size());
1427 ASSERT_EQ(6u, mock_server_->committed_ids().size());
1428 // If this test starts failing, be aware other sort orders could be valid.
1429 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1430 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1431 EXPECT_TRUE(ids_.FromNumber(102) == mock_server_->committed_ids()[2]);
1432 EXPECT_TRUE(ids_.FromNumber(-103) == mock_server_->committed_ids()[3]);
1433 EXPECT_TRUE(ids_.FromNumber(-104) == mock_server_->committed_ids()[4]);
1434 EXPECT_TRUE(ids_.FromNumber(105) == mock_server_->committed_ids()[5]);
1435 }
1436
1437 TEST_F(SyncerTest, TestCommitListOrderingCounterexample) {
1438 syncable::Id child2_id = ids_.NewServerId();
1439
1440 {
1441 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1442 MutableEntry parent(&wtrans, syncable::CREATE, wtrans.root_id(), "P");
1443 ASSERT_TRUE(parent.good());
1444 parent.Put(syncable::IS_UNSYNCED, true);
1445 parent.Put(syncable::IS_DIR, true);
1446 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1447 parent.Put(syncable::ID, parent_id_);
1448 MutableEntry child1(&wtrans, syncable::CREATE, parent_id_, "1");
1449 ASSERT_TRUE(child1.good());
1450 child1.Put(syncable::IS_UNSYNCED, true);
1451 child1.Put(syncable::ID, child_id_);
1452 child1.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1453 MutableEntry child2(&wtrans, syncable::CREATE, parent_id_, "2");
1454 ASSERT_TRUE(child2.good());
1455 child2.Put(syncable::IS_UNSYNCED, true);
1456 child2.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1457 child2.Put(syncable::ID, child2_id);
1458
1459 parent.Put(syncable::BASE_VERSION, 1);
1460 child1.Put(syncable::BASE_VERSION, 1);
1461 child2.Put(syncable::BASE_VERSION, 1);
1462 }
1463
1464 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1465 EXPECT_EQ(3u, session_->status_controller().unsynced_handles().size());
1466 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1467 // If this test starts failing, be aware other sort orders could be valid.
1468 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1469 EXPECT_TRUE(child_id_ == mock_server_->committed_ids()[1]);
1470 EXPECT_TRUE(child2_id == mock_server_->committed_ids()[2]);
1471 }
1472
1473 TEST_F(SyncerTest, TestCommitListOrderingAndNewParent) {
1474 string parent1_name = "1";
1475 string parent2_name = "A";
1476 string child_name = "B";
1477
1478 {
1479 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1480 MutableEntry parent(&wtrans, syncable::CREATE, wtrans.root_id(),
1481 parent1_name);
1482 ASSERT_TRUE(parent.good());
1483 parent.Put(syncable::IS_UNSYNCED, true);
1484 parent.Put(syncable::IS_DIR, true);
1485 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1486 parent.Put(syncable::ID, parent_id_);
1487 parent.Put(syncable::BASE_VERSION, 1);
1488 }
1489
1490 syncable::Id parent2_id = ids_.NewLocalId();
1491 syncable::Id child_id = ids_.NewServerId();
1492 {
1493 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1494 MutableEntry parent2(&wtrans, syncable::CREATE, parent_id_, parent2_name);
1495 ASSERT_TRUE(parent2.good());
1496 parent2.Put(syncable::IS_UNSYNCED, true);
1497 parent2.Put(syncable::IS_DIR, true);
1498 parent2.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1499 parent2.Put(syncable::ID, parent2_id);
1500
1501 MutableEntry child(&wtrans, syncable::CREATE, parent2_id, child_name);
1502 ASSERT_TRUE(child.good());
1503 child.Put(syncable::IS_UNSYNCED, true);
1504 child.Put(syncable::IS_DIR, true);
1505 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1506 child.Put(syncable::ID, child_id);
1507 child.Put(syncable::BASE_VERSION, 1);
1508 }
1509
1510 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1511 EXPECT_EQ(3u, session_->status_controller().unsynced_handles().size());
1512 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1513 // If this test starts failing, be aware other sort orders could be valid.
1514 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1515 EXPECT_TRUE(parent2_id == mock_server_->committed_ids()[1]);
1516 EXPECT_TRUE(child_id == mock_server_->committed_ids()[2]);
1517 {
1518 ReadTransaction rtrans(FROM_HERE, directory());
1519 // Check that things committed correctly.
1520 Entry entry_1(&rtrans, syncable::GET_BY_ID, parent_id_);
1521 EXPECT_EQ(entry_1.Get(NON_UNIQUE_NAME), parent1_name);
1522 // Check that parent2 is a subfolder of parent1.
1523 EXPECT_EQ(1, CountEntriesWithName(&rtrans,
1524 parent_id_,
1525 parent2_name));
1526
1527 // Parent2 was a local ID and thus should have changed on commit!
1528 Entry pre_commit_entry_parent2(&rtrans, syncable::GET_BY_ID, parent2_id);
1529 ASSERT_FALSE(pre_commit_entry_parent2.good());
1530
1531 // Look up the new ID.
1532 Id parent2_committed_id =
1533 GetOnlyEntryWithName(&rtrans, parent_id_, parent2_name);
1534 EXPECT_TRUE(parent2_committed_id.ServerKnows());
1535
1536 Entry child(&rtrans, syncable::GET_BY_ID, child_id);
1537 EXPECT_EQ(parent2_committed_id, child.Get(syncable::PARENT_ID));
1538 }
1539 }
1540
1541 TEST_F(SyncerTest, TestCommitListOrderingAndNewParentAndChild) {
1542 string parent_name = "1";
1543 string parent2_name = "A";
1544 string child_name = "B";
1545
1546 {
1547 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1548 MutableEntry parent(&wtrans,
1549 syncable::CREATE,
1550 wtrans.root_id(),
1551 parent_name);
1552 ASSERT_TRUE(parent.good());
1553 parent.Put(syncable::IS_UNSYNCED, true);
1554 parent.Put(syncable::IS_DIR, true);
1555 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1556 parent.Put(syncable::ID, parent_id_);
1557 parent.Put(syncable::BASE_VERSION, 1);
1558 }
1559
1560 int64 meta_handle_b;
1561 const Id parent2_local_id = ids_.NewLocalId();
1562 const Id child_local_id = ids_.NewLocalId();
1563 {
1564 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
1565 MutableEntry parent2(&wtrans, syncable::CREATE, parent_id_, parent2_name);
1566 ASSERT_TRUE(parent2.good());
1567 parent2.Put(syncable::IS_UNSYNCED, true);
1568 parent2.Put(syncable::IS_DIR, true);
1569 parent2.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1570
1571 parent2.Put(syncable::ID, parent2_local_id);
1572 MutableEntry child(&wtrans, syncable::CREATE, parent2_local_id, child_name);
1573 ASSERT_TRUE(child.good());
1574 child.Put(syncable::IS_UNSYNCED, true);
1575 child.Put(syncable::IS_DIR, true);
1576 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1577 child.Put(syncable::ID, child_local_id);
1578 meta_handle_b = child.Get(syncable::META_HANDLE);
1579 }
1580
1581 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1582 EXPECT_EQ(3u, session_->status_controller().unsynced_handles().size());
1583 ASSERT_EQ(3u, mock_server_->committed_ids().size());
1584 // If this test starts failing, be aware other sort orders could be valid.
1585 EXPECT_TRUE(parent_id_ == mock_server_->committed_ids()[0]);
1586 EXPECT_TRUE(parent2_local_id == mock_server_->committed_ids()[1]);
1587 EXPECT_TRUE(child_local_id == mock_server_->committed_ids()[2]);
1588 {
1589 ReadTransaction rtrans(FROM_HERE, directory());
1590
1591 Entry parent(&rtrans, syncable::GET_BY_ID,
1592 GetOnlyEntryWithName(&rtrans, rtrans.root_id(), parent_name));
1593 ASSERT_TRUE(parent.good());
1594 EXPECT_TRUE(parent.Get(syncable::ID).ServerKnows());
1595
1596 Entry parent2(&rtrans, syncable::GET_BY_ID,
1597 GetOnlyEntryWithName(&rtrans, parent.Get(ID), parent2_name));
1598 ASSERT_TRUE(parent2.good());
1599 EXPECT_TRUE(parent2.Get(syncable::ID).ServerKnows());
1600
1601 // Id changed on commit, so this should fail.
1602 Entry local_parent2_id_entry(&rtrans,
1603 syncable::GET_BY_ID,
1604 parent2_local_id);
1605 ASSERT_FALSE(local_parent2_id_entry.good());
1606
1607 Entry entry_b(&rtrans, syncable::GET_BY_HANDLE, meta_handle_b);
1608 EXPECT_TRUE(entry_b.Get(syncable::ID).ServerKnows());
1609 EXPECT_TRUE(parent2.Get(syncable::ID) == entry_b.Get(syncable::PARENT_ID));
1610 }
1611 }
1612
1613 TEST_F(SyncerTest, UpdateWithZeroLengthName) {
1614 // One illegal update
1615 mock_server_->AddUpdateDirectory(1, 0, "", 1, 10);
1616 // And one legal one that we're going to delete.
1617 mock_server_->AddUpdateDirectory(2, 0, "FOO", 1, 10);
1618 SyncShareAsDelegate();
1619 // Delete the legal one. The new update has a null name.
1620 mock_server_->AddUpdateDirectory(2, 0, "", 2, 20);
1621 mock_server_->SetLastUpdateDeleted();
1622 SyncShareAsDelegate();
1623 }
1624
1625 TEST_F(SyncerTest, TestBasicUpdate) {
1626 string id = "some_id";
1627 string parent_id = "0";
1628 string name = "in_root";
1629 int64 version = 10;
1630 int64 timestamp = 10;
1631 mock_server_->AddUpdateDirectory(id, parent_id, name, version, timestamp);
1632
1633 SyncShareAsDelegate();
1634 {
1635 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1636 Entry entry(&trans, GET_BY_ID,
1637 syncable::Id::CreateFromServerId("some_id"));
1638 ASSERT_TRUE(entry.good());
1639 EXPECT_TRUE(entry.Get(IS_DIR));
1640 EXPECT_TRUE(entry.Get(SERVER_VERSION) == version);
1641 EXPECT_TRUE(entry.Get(BASE_VERSION) == version);
1642 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
1643 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
1644 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
1645 EXPECT_FALSE(entry.Get(IS_DEL));
1646 }
1647 }
1648
1649 TEST_F(SyncerTest, IllegalAndLegalUpdates) {
1650 Id root = TestIdFactory::root();
1651 // Should apply just fine.
1652 mock_server_->AddUpdateDirectory(1, 0, "in_root", 10, 10);
1653
1654 // Same name. But this SHOULD work.
1655 mock_server_->AddUpdateDirectory(2, 0, "in_root", 10, 10);
1656
1657 // Unknown parent: should never be applied. "-80" is a legal server ID,
1658 // because any string sent by the server is a legal server ID in the sync
1659 // protocol, but it's not the ID of any item known to the client. This
1660 // update should succeed validation, but be stuck in the unapplied state
1661 // until an item with the server ID "-80" arrives.
1662 mock_server_->AddUpdateDirectory(3, -80, "bad_parent", 10, 10);
1663
1664 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1665 StatusController* status = session_->mutable_status_controller();
1666
1667 // Id 3 should be in conflict now.
1668 EXPECT_EQ(1, status->TotalNumConflictingItems());
1669 {
1670 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
1671 ASSERT_TRUE(status->conflict_progress());
1672 EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
1673 }
1674
1675 // These entries will be used in the second set of updates.
1676 mock_server_->AddUpdateDirectory(4, 0, "newer_version", 20, 10);
1677 mock_server_->AddUpdateDirectory(5, 0, "circular1", 10, 10);
1678 mock_server_->AddUpdateDirectory(6, 5, "circular2", 10, 10);
1679 mock_server_->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10);
1680 mock_server_->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10);
1681 mock_server_->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10);
1682
1683 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1684 // The three items with an unresolved parent should be unapplied (3, 9, 100).
1685 // The name clash should also still be in conflict.
1686 EXPECT_EQ(3, status->TotalNumConflictingItems());
1687 {
1688 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
1689 ASSERT_TRUE(status->conflict_progress());
1690 EXPECT_EQ(3, status->conflict_progress()->HierarchyConflictingItemsSize());
1691 }
1692
1693 {
1694 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1695 // Even though it has the same name, it should work.
1696 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
1697 ASSERT_TRUE(name_clash.good());
1698 EXPECT_FALSE(name_clash.Get(IS_UNAPPLIED_UPDATE))
1699 << "Duplicate name SHOULD be OK.";
1700
1701 Entry bad_parent(&trans, GET_BY_ID, ids_.FromNumber(3));
1702 ASSERT_TRUE(bad_parent.good());
1703 EXPECT_TRUE(bad_parent.Get(IS_UNAPPLIED_UPDATE))
1704 << "child of unknown parent should be in conflict";
1705
1706 Entry bad_parent_child(&trans, GET_BY_ID, ids_.FromNumber(9));
1707 ASSERT_TRUE(bad_parent_child.good());
1708 EXPECT_TRUE(bad_parent_child.Get(IS_UNAPPLIED_UPDATE))
1709 << "grandchild of unknown parent should be in conflict";
1710
1711 Entry bad_parent_child2(&trans, GET_BY_ID, ids_.FromNumber(100));
1712 ASSERT_TRUE(bad_parent_child2.good());
1713 EXPECT_TRUE(bad_parent_child2.Get(IS_UNAPPLIED_UPDATE))
1714 << "great-grandchild of unknown parent should be in conflict";
1715 }
1716
1717 // Updating 1 should not affect item 2 of the same name.
1718 mock_server_->AddUpdateDirectory(1, 0, "new_name", 20, 20);
1719
1720 // Moving 5 under 6 will create a cycle: a conflict.
1721 mock_server_->AddUpdateDirectory(5, 6, "circular3", 20, 20);
1722
1723 // Flip the is_dir bit: should fail verify & be dropped.
1724 mock_server_->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20);
1725 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1726
1727 // Version number older than last known: should fail verify & be dropped.
1728 mock_server_->AddUpdateDirectory(4, 0, "old_version", 10, 10);
1729 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
1730 {
1731 ReadTransaction trans(FROM_HERE, directory());
1732
1733 Entry still_a_dir(&trans, GET_BY_ID, ids_.FromNumber(10));
1734 ASSERT_TRUE(still_a_dir.good());
1735 EXPECT_FALSE(still_a_dir.Get(IS_UNAPPLIED_UPDATE));
1736 EXPECT_EQ(10u, still_a_dir.Get(BASE_VERSION));
1737 EXPECT_EQ(10u, still_a_dir.Get(SERVER_VERSION));
1738 EXPECT_TRUE(still_a_dir.Get(IS_DIR));
1739
1740 Entry rename(&trans, GET_BY_ID, ids_.FromNumber(1));
1741 ASSERT_TRUE(rename.good());
1742 EXPECT_EQ(root, rename.Get(PARENT_ID));
1743 EXPECT_EQ("new_name", rename.Get(NON_UNIQUE_NAME));
1744 EXPECT_FALSE(rename.Get(IS_UNAPPLIED_UPDATE));
1745 EXPECT_TRUE(ids_.FromNumber(1) == rename.Get(ID));
1746 EXPECT_EQ(20u, rename.Get(BASE_VERSION));
1747
1748 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2));
1749 ASSERT_TRUE(name_clash.good());
1750 EXPECT_EQ(root, name_clash.Get(PARENT_ID));
1751 EXPECT_TRUE(ids_.FromNumber(2) == name_clash.Get(ID));
1752 EXPECT_EQ(10u, name_clash.Get(BASE_VERSION));
1753 EXPECT_EQ("in_root", name_clash.Get(NON_UNIQUE_NAME));
1754
1755 Entry ignored_old_version(&trans, GET_BY_ID, ids_.FromNumber(4));
1756 ASSERT_TRUE(ignored_old_version.good());
1757 EXPECT_TRUE(
1758 ignored_old_version.Get(NON_UNIQUE_NAME) == "newer_version");
1759 EXPECT_FALSE(ignored_old_version.Get(IS_UNAPPLIED_UPDATE));
1760 EXPECT_EQ(20u, ignored_old_version.Get(BASE_VERSION));
1761
1762 Entry circular_parent_issue(&trans, GET_BY_ID, ids_.FromNumber(5));
1763 ASSERT_TRUE(circular_parent_issue.good());
1764 EXPECT_TRUE(circular_parent_issue.Get(IS_UNAPPLIED_UPDATE))
1765 << "circular move should be in conflict";
1766 EXPECT_TRUE(circular_parent_issue.Get(PARENT_ID) == root_id_);
1767 EXPECT_TRUE(circular_parent_issue.Get(SERVER_PARENT_ID) ==
1768 ids_.FromNumber(6));
1769 EXPECT_EQ(10u, circular_parent_issue.Get(BASE_VERSION));
1770
1771 Entry circular_parent_target(&trans, GET_BY_ID, ids_.FromNumber(6));
1772 ASSERT_TRUE(circular_parent_target.good());
1773 EXPECT_FALSE(circular_parent_target.Get(IS_UNAPPLIED_UPDATE));
1774 EXPECT_TRUE(circular_parent_issue.Get(ID) ==
1775 circular_parent_target.Get(PARENT_ID));
1776 EXPECT_EQ(10u, circular_parent_target.Get(BASE_VERSION));
1777 }
1778
1779 EXPECT_FALSE(saw_syncer_event_);
1780 EXPECT_EQ(4, status->TotalNumConflictingItems());
1781 {
1782 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
1783 ASSERT_TRUE(status->conflict_progress());
1784 EXPECT_EQ(4, status->conflict_progress()->HierarchyConflictingItemsSize());
1785 }
1786 }
1787
1788 TEST_F(SyncerTest, CommitTimeRename) {
1789 int64 metahandle_folder;
1790 int64 metahandle_new_entry;
1791
1792 // Create a folder and an entry.
1793 {
1794 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1795 MutableEntry parent(&trans, CREATE, root_id_, "Folder");
1796 ASSERT_TRUE(parent.good());
1797 parent.Put(IS_DIR, true);
1798 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
1799 parent.Put(IS_UNSYNCED, true);
1800 metahandle_folder = parent.Get(META_HANDLE);
1801
1802 MutableEntry entry(&trans, CREATE, parent.Get(ID), "new_entry");
1803 ASSERT_TRUE(entry.good());
1804 metahandle_new_entry = entry.Get(META_HANDLE);
1805 WriteTestDataToEntry(&trans, &entry);
1806 }
1807
1808 // Mix in a directory creation too for later.
1809 mock_server_->AddUpdateDirectory(2, 0, "dir_in_root", 10, 10);
1810 mock_server_->SetCommitTimeRename("renamed_");
1811 SyncShareAsDelegate();
1812
1813 // Verify it was correctly renamed.
1814 {
1815 ReadTransaction trans(FROM_HERE, directory());
1816 Entry entry_folder(&trans, GET_BY_HANDLE, metahandle_folder);
1817 ASSERT_TRUE(entry_folder.good());
1818 EXPECT_EQ("renamed_Folder", entry_folder.Get(NON_UNIQUE_NAME));
1819
1820 Entry entry_new(&trans, GET_BY_HANDLE, metahandle_new_entry);
1821 ASSERT_TRUE(entry_new.good());
1822 EXPECT_EQ(entry_folder.Get(ID), entry_new.Get(PARENT_ID));
1823 EXPECT_EQ("renamed_new_entry", entry_new.Get(NON_UNIQUE_NAME));
1824
1825 // And that the unrelated directory creation worked without a rename.
1826 Entry new_dir(&trans, GET_BY_ID, ids_.FromNumber(2));
1827 EXPECT_TRUE(new_dir.good());
1828 EXPECT_EQ("dir_in_root", new_dir.Get(NON_UNIQUE_NAME));
1829 }
1830 }
1831
1832
1833 TEST_F(SyncerTest, CommitTimeRenameI18N) {
1834 // This is utf-8 for the diacritized Internationalization.
1835 const char* i18nString = "\xc3\x8e\xc3\xb1\x74\xc3\xa9\x72\xc3\xb1"
1836 "\xc3\xa5\x74\xc3\xae\xc3\xb6\xc3\xb1\xc3\xa5\x6c\xc3\xae"
1837 "\xc2\x9e\xc3\xa5\x74\xc3\xae\xc3\xb6\xc3\xb1";
1838
1839 int64 metahandle;
1840 // Create a folder, expect a commit time rename.
1841 {
1842 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1843 MutableEntry parent(&trans, CREATE, root_id_, "Folder");
1844 ASSERT_TRUE(parent.good());
1845 parent.Put(IS_DIR, true);
1846 parent.Put(SPECIFICS, DefaultBookmarkSpecifics());
1847 parent.Put(IS_UNSYNCED, true);
1848 metahandle = parent.Get(META_HANDLE);
1849 }
1850
1851 mock_server_->SetCommitTimeRename(i18nString);
1852 SyncShareAsDelegate();
1853
1854 // Verify it was correctly renamed.
1855 {
1856 ReadTransaction trans(FROM_HERE, directory());
1857 string expected_folder_name(i18nString);
1858 expected_folder_name.append("Folder");
1859
1860
1861 Entry entry_folder(&trans, GET_BY_HANDLE, metahandle);
1862 ASSERT_TRUE(entry_folder.good());
1863 EXPECT_EQ(expected_folder_name, entry_folder.Get(NON_UNIQUE_NAME));
1864 }
1865 }
1866
1867 // A commit with a lost response produces an update that has to be reunited with
1868 // its parent.
1869 TEST_F(SyncerTest, CommitReuniteUpdateAdjustsChildren) {
1870 // Create a folder in the root.
1871 int64 metahandle_folder;
1872 {
1873 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1874 MutableEntry entry(&trans, CREATE, trans.root_id(), "new_folder");
1875 ASSERT_TRUE(entry.good());
1876 entry.Put(IS_DIR, true);
1877 entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
1878 entry.Put(IS_UNSYNCED, true);
1879 metahandle_folder = entry.Get(META_HANDLE);
1880 }
1881
1882 // Verify it and pull the ID out of the folder.
1883 syncable::Id folder_id;
1884 int64 metahandle_entry;
1885 {
1886 ReadTransaction trans(FROM_HERE, directory());
1887 Entry entry(&trans, GET_BY_HANDLE, metahandle_folder);
1888 ASSERT_TRUE(entry.good());
1889 folder_id = entry.Get(ID);
1890 ASSERT_TRUE(!folder_id.ServerKnows());
1891 }
1892
1893 // Create an entry in the newly created folder.
1894 {
1895 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1896 MutableEntry entry(&trans, CREATE, folder_id, "new_entry");
1897 ASSERT_TRUE(entry.good());
1898 metahandle_entry = entry.Get(META_HANDLE);
1899 WriteTestDataToEntry(&trans, &entry);
1900 }
1901
1902 // Verify it and pull the ID out of the entry.
1903 syncable::Id entry_id;
1904 {
1905 ReadTransaction trans(FROM_HERE, directory());
1906 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
1907 ASSERT_TRUE(entry.good());
1908 EXPECT_EQ(folder_id, entry.Get(PARENT_ID));
1909 EXPECT_EQ("new_entry", entry.Get(NON_UNIQUE_NAME));
1910 entry_id = entry.Get(ID);
1911 EXPECT_TRUE(!entry_id.ServerKnows());
1912 VerifyTestDataInEntry(&trans, &entry);
1913 }
1914
1915 // Now, to emulate a commit response failure, we just don't commit it.
1916 int64 new_version = 150; // any larger value.
1917 int64 timestamp = 20; // arbitrary value.
1918 syncable::Id new_folder_id =
1919 syncable::Id::CreateFromServerId("folder_server_id");
1920
1921 // The following update should cause the folder to both apply the update, as
1922 // well as reassociate the id.
1923 mock_server_->AddUpdateDirectory(new_folder_id, root_id_,
1924 "new_folder", new_version, timestamp);
1925 mock_server_->SetLastUpdateOriginatorFields(
1926 directory()->cache_guid(), folder_id.GetServerId());
1927
1928 // We don't want it accidentally committed, just the update applied.
1929 mock_server_->set_conflict_all_commits(true);
1930
1931 // Alright! Apply that update!
1932 SyncShareAsDelegate();
1933 {
1934 // The folder's ID should have been updated.
1935 ReadTransaction trans(FROM_HERE, directory());
1936 Entry folder(&trans, GET_BY_HANDLE, metahandle_folder);
1937 ASSERT_TRUE(folder.good());
1938 EXPECT_EQ("new_folder", folder.Get(NON_UNIQUE_NAME));
1939 EXPECT_TRUE(new_version == folder.Get(BASE_VERSION));
1940 EXPECT_TRUE(new_folder_id == folder.Get(ID));
1941 EXPECT_TRUE(folder.Get(ID).ServerKnows());
1942 EXPECT_EQ(trans.root_id(), folder.Get(PARENT_ID));
1943
1944 // Since it was updated, the old folder should not exist.
1945 Entry old_dead_folder(&trans, GET_BY_ID, folder_id);
1946 EXPECT_FALSE(old_dead_folder.good());
1947
1948 // The child's parent should have changed.
1949 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry);
1950 ASSERT_TRUE(entry.good());
1951 EXPECT_EQ("new_entry", entry.Get(NON_UNIQUE_NAME));
1952 EXPECT_EQ(new_folder_id, entry.Get(PARENT_ID));
1953 EXPECT_TRUE(!entry.Get(ID).ServerKnows());
1954 VerifyTestDataInEntry(&trans, &entry);
1955 }
1956 }
1957
1958 // A commit with a lost response produces an update that has to be reunited with
1959 // its parent.
1960 TEST_F(SyncerTest, CommitReuniteUpdate) {
1961 // Create an entry in the root.
1962 int64 entry_metahandle;
1963 {
1964 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
1965 MutableEntry entry(&trans, CREATE, trans.root_id(), "new_entry");
1966 ASSERT_TRUE(entry.good());
1967 entry_metahandle = entry.Get(META_HANDLE);
1968 WriteTestDataToEntry(&trans, &entry);
1969 }
1970
1971 // Verify it and pull the ID out.
1972 syncable::Id entry_id;
1973 {
1974 ReadTransaction trans(FROM_HERE, directory());
1975
1976 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
1977 ASSERT_TRUE(entry.good());
1978 entry_id = entry.Get(ID);
1979 EXPECT_TRUE(!entry_id.ServerKnows());
1980 VerifyTestDataInEntry(&trans, &entry);
1981 }
1982
1983 // Now, to emulate a commit response failure, we just don't commit it.
1984 int64 new_version = 150; // any larger value.
1985 int64 timestamp = 20; // arbitrary value.
1986 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
1987
1988 // Generate an update from the server with a relevant ID reassignment.
1989 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
1990 "new_entry", new_version, timestamp);
1991 mock_server_->SetLastUpdateOriginatorFields(
1992 directory()->cache_guid(), entry_id.GetServerId());
1993
1994 // We don't want it accidentally committed, just the update applied.
1995 mock_server_->set_conflict_all_commits(true);
1996
1997 // Alright! Apply that update!
1998 SyncShareAsDelegate();
1999 {
2000 ReadTransaction trans(FROM_HERE, directory());
2001 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
2002 ASSERT_TRUE(entry.good());
2003 EXPECT_TRUE(new_version == entry.Get(BASE_VERSION));
2004 EXPECT_TRUE(new_entry_id == entry.Get(ID));
2005 EXPECT_EQ("new_entry", entry.Get(NON_UNIQUE_NAME));
2006 }
2007 }
2008
2009 // A commit with a lost response must work even if the local entry was deleted
2010 // before the update is applied. We should not duplicate the local entry in
2011 // this case, but just create another one alongside. We may wish to examine
2012 // this behavior in the future as it can create hanging uploads that never
2013 // finish, that must be cleaned up on the server side after some time.
2014 TEST_F(SyncerTest, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry) {
2015 // Create a entry in the root.
2016 int64 entry_metahandle;
2017 {
2018 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2019 MutableEntry entry(&trans, CREATE, trans.root_id(), "new_entry");
2020 ASSERT_TRUE(entry.good());
2021 entry_metahandle = entry.Get(META_HANDLE);
2022 WriteTestDataToEntry(&trans, &entry);
2023 }
2024 // Verify it and pull the ID out.
2025 syncable::Id entry_id;
2026 {
2027 ReadTransaction trans(FROM_HERE, directory());
2028 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle);
2029 ASSERT_TRUE(entry.good());
2030 entry_id = entry.Get(ID);
2031 EXPECT_TRUE(!entry_id.ServerKnows());
2032 VerifyTestDataInEntry(&trans, &entry);
2033 }
2034
2035 // Now, to emulate a commit response failure, we just don't commit it.
2036 int64 new_version = 150; // any larger value.
2037 int64 timestamp = 20; // arbitrary value.
2038 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id");
2039
2040 // Generate an update from the server with a relevant ID reassignment.
2041 mock_server_->AddUpdateBookmark(new_entry_id, root_id_,
2042 "new_entry", new_version, timestamp);
2043 mock_server_->SetLastUpdateOriginatorFields(
2044 directory()->cache_guid(),
2045 entry_id.GetServerId());
2046
2047 // We don't want it accidentally committed, just the update applied.
2048 mock_server_->set_conflict_all_commits(true);
2049
2050 // Purposefully delete the entry now before the update application finishes.
2051 {
2052 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2053 Id new_entry_id = GetOnlyEntryWithName(
2054 &trans, trans.root_id(), "new_entry");
2055 MutableEntry entry(&trans, GET_BY_ID, new_entry_id);
2056 ASSERT_TRUE(entry.good());
2057 entry.Put(syncable::IS_DEL, true);
2058 }
2059
2060 // Just don't CHECK fail in sync, have the update split.
2061 SyncShareAsDelegate();
2062 {
2063 ReadTransaction trans(FROM_HERE, directory());
2064 Id new_entry_id = GetOnlyEntryWithName(
2065 &trans, trans.root_id(), "new_entry");
2066 Entry entry(&trans, GET_BY_ID, new_entry_id);
2067 ASSERT_TRUE(entry.good());
2068 EXPECT_FALSE(entry.Get(IS_DEL));
2069
2070 Entry old_entry(&trans, GET_BY_ID, entry_id);
2071 ASSERT_TRUE(old_entry.good());
2072 EXPECT_TRUE(old_entry.Get(IS_DEL));
2073 }
2074 }
2075
2076 // TODO(chron): Add more unsanitized name tests.
2077 TEST_F(SyncerTest, ConflictMatchingEntryHandlesUnsanitizedNames) {
2078 mock_server_->AddUpdateDirectory(1, 0, "A/A", 10, 10);
2079 mock_server_->AddUpdateDirectory(2, 0, "B/B", 10, 10);
2080 mock_server_->set_conflict_all_commits(true);
2081 SyncShareAsDelegate();
2082 {
2083 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2084
2085 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2086 ASSERT_TRUE(A.good());
2087 A.Put(IS_UNSYNCED, true);
2088 A.Put(IS_UNAPPLIED_UPDATE, true);
2089 A.Put(SERVER_VERSION, 20);
2090
2091 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2092 ASSERT_TRUE(B.good());
2093 B.Put(IS_UNAPPLIED_UPDATE, true);
2094 B.Put(SERVER_VERSION, 20);
2095 }
2096 LoopSyncShare();
2097 saw_syncer_event_ = false;
2098 mock_server_->set_conflict_all_commits(false);
2099
2100 {
2101 ReadTransaction trans(FROM_HERE, directory());
2102
2103 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
2104 ASSERT_TRUE(A.good());
2105 EXPECT_TRUE(A.Get(IS_UNSYNCED) == false);
2106 EXPECT_TRUE(A.Get(IS_UNAPPLIED_UPDATE) == false);
2107 EXPECT_TRUE(A.Get(SERVER_VERSION) == 20);
2108
2109 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2110 ASSERT_TRUE(B.good());
2111 EXPECT_TRUE(B.Get(IS_UNSYNCED) == false);
2112 EXPECT_TRUE(B.Get(IS_UNAPPLIED_UPDATE) == false);
2113 EXPECT_TRUE(B.Get(SERVER_VERSION) == 20);
2114 }
2115 }
2116
2117 TEST_F(SyncerTest, ConflictMatchingEntryHandlesNormalNames) {
2118 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10);
2119 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10);
2120 mock_server_->set_conflict_all_commits(true);
2121 SyncShareAsDelegate();
2122 {
2123 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2124
2125 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2126 ASSERT_TRUE(A.good());
2127 A.Put(IS_UNSYNCED, true);
2128 A.Put(IS_UNAPPLIED_UPDATE, true);
2129 A.Put(SERVER_VERSION, 20);
2130
2131 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2132 ASSERT_TRUE(B.good());
2133 B.Put(IS_UNAPPLIED_UPDATE, true);
2134 B.Put(SERVER_VERSION, 20);
2135 }
2136 LoopSyncShare();
2137 saw_syncer_event_ = false;
2138 mock_server_->set_conflict_all_commits(false);
2139
2140 {
2141 ReadTransaction trans(FROM_HERE, directory());
2142
2143 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1));
2144 ASSERT_TRUE(A.good());
2145 EXPECT_TRUE(A.Get(IS_UNSYNCED) == false);
2146 EXPECT_TRUE(A.Get(IS_UNAPPLIED_UPDATE) == false);
2147 EXPECT_TRUE(A.Get(SERVER_VERSION) == 20);
2148
2149 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2150 ASSERT_TRUE(B.good());
2151 EXPECT_TRUE(B.Get(IS_UNSYNCED) == false);
2152 EXPECT_TRUE(B.Get(IS_UNAPPLIED_UPDATE) == false);
2153 EXPECT_TRUE(B.Get(SERVER_VERSION) == 20);
2154 }
2155 }
2156
2157 TEST_F(SyncerTest, ReverseFolderOrderingTest) {
2158 mock_server_->AddUpdateDirectory(4, 3, "ggchild", 10, 10);
2159 mock_server_->AddUpdateDirectory(3, 2, "gchild", 10, 10);
2160 mock_server_->AddUpdateDirectory(5, 4, "gggchild", 10, 10);
2161 mock_server_->AddUpdateDirectory(2, 1, "child", 10, 10);
2162 mock_server_->AddUpdateDirectory(1, 0, "parent", 10, 10);
2163 LoopSyncShare();
2164 ReadTransaction trans(FROM_HERE, directory());
2165
2166 Id child_id = GetOnlyEntryWithName(
2167 &trans, ids_.FromNumber(4), "gggchild");
2168 Entry child(&trans, GET_BY_ID, child_id);
2169 ASSERT_TRUE(child.good());
2170 }
2171
2172 class EntryCreatedInNewFolderTest : public SyncerTest {
2173 public:
2174 void CreateFolderInBob() {
2175 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2176 MutableEntry bob(&trans,
2177 syncable::GET_BY_ID,
2178 GetOnlyEntryWithName(&trans,
2179 TestIdFactory::root(),
2180 "bob"));
2181 CHECK(bob.good());
2182
2183 MutableEntry entry2(&trans, syncable::CREATE, bob.Get(syncable::ID),
2184 "bob");
2185 CHECK(entry2.good());
2186 entry2.Put(syncable::IS_DIR, true);
2187 entry2.Put(syncable::IS_UNSYNCED, true);
2188 entry2.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
2189 }
2190 };
2191
2192 TEST_F(EntryCreatedInNewFolderTest, EntryCreatedInNewFolderMidSync) {
2193 directory()->set_store_birthday(mock_server_->store_birthday());
2194 {
2195 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2196 MutableEntry entry(&trans, syncable::CREATE, trans.root_id(),
2197 "bob");
2198 ASSERT_TRUE(entry.good());
2199 entry.Put(syncable::IS_DIR, true);
2200 entry.Put(syncable::IS_UNSYNCED, true);
2201 entry.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
2202 }
2203
2204 mock_server_->SetMidCommitCallback(
2205 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob,
2206 base::Unretained(this)));
2207 syncer_->SyncShare(session_.get(), BUILD_COMMIT_REQUEST, SYNCER_END);
2208 EXPECT_EQ(1u, mock_server_->committed_ids().size());
2209 {
2210 ReadTransaction trans(FROM_HERE, directory());
2211 Entry parent_entry(&trans, syncable::GET_BY_ID,
2212 GetOnlyEntryWithName(&trans, TestIdFactory::root(), "bob"));
2213 ASSERT_TRUE(parent_entry.good());
2214
2215 Id child_id =
2216 GetOnlyEntryWithName(&trans, parent_entry.Get(ID), "bob");
2217 Entry child(&trans, syncable::GET_BY_ID, child_id);
2218 ASSERT_TRUE(child.good());
2219 EXPECT_EQ(parent_entry.Get(ID), child.Get(PARENT_ID));
2220 }
2221 }
2222
2223 TEST_F(SyncerTest, NegativeIDInUpdate) {
2224 mock_server_->AddUpdateBookmark(-10, 0, "bad", 40, 40);
2225 SyncShareAsDelegate();
2226 // The negative id would make us CHECK!
2227 }
2228
2229 TEST_F(SyncerTest, UnappliedUpdateOnCreatedItemItemDoesNotCrash) {
2230 int64 metahandle_fred;
2231 {
2232 // Create an item.
2233 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2234 MutableEntry fred_match(&trans, CREATE, trans.root_id(),
2235 "fred_match");
2236 ASSERT_TRUE(fred_match.good());
2237 metahandle_fred = fred_match.Get(META_HANDLE);
2238 WriteTestDataToEntry(&trans, &fred_match);
2239 }
2240 // Commit it.
2241 SyncShareAsDelegate();
2242 EXPECT_EQ(1u, mock_server_->committed_ids().size());
2243 mock_server_->set_conflict_all_commits(true);
2244 syncable::Id fred_match_id;
2245 {
2246 // Now receive a change from outside.
2247 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2248 MutableEntry fred_match(&trans, GET_BY_HANDLE, metahandle_fred);
2249 ASSERT_TRUE(fred_match.good());
2250 EXPECT_TRUE(fred_match.Get(ID).ServerKnows());
2251 fred_match_id = fred_match.Get(ID);
2252 mock_server_->AddUpdateBookmark(fred_match_id, trans.root_id(),
2253 "fred_match", 40, 40);
2254 }
2255 // Run the syncer.
2256 for (int i = 0 ; i < 30 ; ++i) {
2257 SyncShareAsDelegate();
2258 }
2259 }
2260
2261 /**
2262 * In the event that we have a double changed entry, that is changed on both
2263 * the client and the server, the conflict resolver should just drop one of
2264 * them and accept the other.
2265 */
2266
2267 TEST_F(SyncerTest, DoublyChangedWithResolver) {
2268 {
2269 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2270 MutableEntry parent(&wtrans, syncable::CREATE, root_id_, "Folder");
2271 ASSERT_TRUE(parent.good());
2272 parent.Put(syncable::IS_DIR, true);
2273 parent.Put(syncable::ID, parent_id_);
2274 parent.Put(syncable::BASE_VERSION, 5);
2275 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
2276 MutableEntry child(&wtrans, syncable::CREATE, parent_id_, "Pete.htm");
2277 ASSERT_TRUE(child.good());
2278 child.Put(syncable::ID, child_id_);
2279 child.Put(syncable::BASE_VERSION, 10);
2280 WriteTestDataToEntry(&wtrans, &child);
2281 }
2282 mock_server_->AddUpdateBookmark(child_id_, parent_id_, "Pete2.htm", 11, 10);
2283 mock_server_->set_conflict_all_commits(true);
2284 LoopSyncShare();
2285 syncable::Directory::ChildHandles children;
2286 {
2287 ReadTransaction trans(FROM_HERE, directory());
2288 directory()->GetChildHandlesById(&trans, parent_id_, &children);
2289 // We expect the conflict resolver to preserve the local entry.
2290 Entry child(&trans, syncable::GET_BY_ID, child_id_);
2291 ASSERT_TRUE(child.good());
2292 EXPECT_TRUE(child.Get(syncable::IS_UNSYNCED));
2293 EXPECT_FALSE(child.Get(syncable::IS_UNAPPLIED_UPDATE));
2294 EXPECT_TRUE(child.Get(SPECIFICS).has_bookmark());
2295 EXPECT_EQ("Pete.htm", child.Get(NON_UNIQUE_NAME));
2296 VerifyTestBookmarkDataInEntry(&child);
2297 }
2298
2299 // Only one entry, since we just overwrite one.
2300 EXPECT_EQ(1u, children.size());
2301 saw_syncer_event_ = false;
2302 }
2303
2304 // We got this repro case when someone was editing bookmarks while sync was
2305 // occuring. The entry had changed out underneath the user.
2306 TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) {
2307 const base::Time& test_time = ProtoTimeToTime(123456);
2308 int64 entry_metahandle;
2309 {
2310 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2311 MutableEntry entry(&wtrans, syncable::CREATE, root_id_, "Pete");
2312 ASSERT_TRUE(entry.good());
2313 EXPECT_FALSE(entry.Get(ID).ServerKnows());
2314 entry.Put(syncable::IS_DIR, true);
2315 entry.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
2316 entry.Put(syncable::IS_UNSYNCED, true);
2317 entry.Put(syncable::MTIME, test_time);
2318 entry_metahandle = entry.Get(META_HANDLE);
2319 }
2320 SyncShareAsDelegate();
2321 syncable::Id id;
2322 int64 version;
2323 int64 server_position_in_parent;
2324 {
2325 ReadTransaction trans(FROM_HERE, directory());
2326 Entry entry(&trans, syncable::GET_BY_HANDLE, entry_metahandle);
2327 ASSERT_TRUE(entry.good());
2328 id = entry.Get(ID);
2329 EXPECT_TRUE(id.ServerKnows());
2330 version = entry.Get(BASE_VERSION);
2331 server_position_in_parent = entry.Get(SERVER_POSITION_IN_PARENT);
2332 }
2333 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
2334 EXPECT_EQ("Pete", update->name());
2335 EXPECT_EQ(id.GetServerId(), update->id_string());
2336 EXPECT_EQ(root_id_.GetServerId(), update->parent_id_string());
2337 EXPECT_EQ(version, update->version());
2338 EXPECT_EQ(server_position_in_parent, update->position_in_parent());
2339 SyncShareAsDelegate();
2340 {
2341 ReadTransaction trans(FROM_HERE, directory());
2342 Entry entry(&trans, syncable::GET_BY_ID, id);
2343 ASSERT_TRUE(entry.good());
2344 EXPECT_TRUE(entry.Get(MTIME) == test_time);
2345 }
2346 }
2347
2348 TEST_F(SyncerTest, ParentAndChildBothMatch) {
2349 const syncable::FullModelTypeSet all_types =
2350 syncable::FullModelTypeSet::All();
2351 syncable::Id parent_id = ids_.NewServerId();
2352 syncable::Id child_id = ids_.NewServerId();
2353
2354 {
2355 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2356 MutableEntry parent(&wtrans, CREATE, root_id_, "Folder");
2357 ASSERT_TRUE(parent.good());
2358 parent.Put(IS_DIR, true);
2359 parent.Put(IS_UNSYNCED, true);
2360 parent.Put(ID, parent_id);
2361 parent.Put(BASE_VERSION, 1);
2362 parent.Put(SPECIFICS, DefaultBookmarkSpecifics());
2363
2364 MutableEntry child(&wtrans, CREATE, parent.Get(ID), "test.htm");
2365 ASSERT_TRUE(child.good());
2366 child.Put(ID, child_id);
2367 child.Put(BASE_VERSION, 1);
2368 child.Put(SPECIFICS, DefaultBookmarkSpecifics());
2369 WriteTestDataToEntry(&wtrans, &child);
2370 }
2371 mock_server_->AddUpdateDirectory(parent_id, root_id_, "Folder", 10, 10);
2372 mock_server_->AddUpdateBookmark(child_id, parent_id, "test.htm", 10, 10);
2373 mock_server_->set_conflict_all_commits(true);
2374 SyncShareAsDelegate();
2375 SyncShareAsDelegate();
2376 SyncShareAsDelegate();
2377 {
2378 ReadTransaction trans(FROM_HERE, directory());
2379 Directory::ChildHandles children;
2380 directory()->GetChildHandlesById(&trans, root_id_, &children);
2381 EXPECT_EQ(1u, children.size());
2382 directory()->GetChildHandlesById(&trans, parent_id, &children);
2383 EXPECT_EQ(1u, children.size());
2384 Directory::UnappliedUpdateMetaHandles unapplied;
2385 directory()->GetUnappliedUpdateMetaHandles(&trans, all_types, &unapplied);
2386 EXPECT_EQ(0u, unapplied.size());
2387 syncable::Directory::UnsyncedMetaHandles unsynced;
2388 directory()->GetUnsyncedMetaHandles(&trans, &unsynced);
2389 EXPECT_EQ(0u, unsynced.size());
2390 saw_syncer_event_ = false;
2391 }
2392 }
2393
2394 TEST_F(SyncerTest, CommittingNewDeleted) {
2395 {
2396 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2397 MutableEntry entry(&trans, CREATE, trans.root_id(), "bob");
2398 entry.Put(IS_UNSYNCED, true);
2399 entry.Put(IS_DEL, true);
2400 }
2401 SyncShareAsDelegate();
2402 EXPECT_EQ(0u, mock_server_->committed_ids().size());
2403 }
2404
2405 // Original problem synopsis:
2406 // Check failed: entry->Get(BASE_VERSION) <= entry->Get(SERVER_VERSION)
2407 // Client creates entry, client finishes committing entry. Between
2408 // commit and getting update back, we delete the entry.
2409 // We get the update for the entry, but the local one was modified
2410 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set.
2411 // We commit deletion and get a new version number.
2412 // We apply unapplied updates again before we get the update about the deletion.
2413 // This means we have an unapplied update where server_version < base_version.
2414 TEST_F(SyncerTest, UnappliedUpdateDuringCommit) {
2415 // This test is a little fake.
2416 {
2417 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2418 MutableEntry entry(&trans, CREATE, trans.root_id(), "bob");
2419 entry.Put(ID, ids_.FromNumber(20));
2420 entry.Put(BASE_VERSION, 1);
2421 entry.Put(SERVER_VERSION, 1);
2422 entry.Put(SERVER_PARENT_ID, ids_.FromNumber(9999)); // Bad parent.
2423 entry.Put(IS_UNSYNCED, true);
2424 entry.Put(IS_UNAPPLIED_UPDATE, true);
2425 entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
2426 entry.Put(SERVER_SPECIFICS, DefaultBookmarkSpecifics());
2427 entry.Put(IS_DEL, false);
2428 }
2429 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
2430 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
2431 EXPECT_EQ(1, session_->status_controller().TotalNumConflictingItems());
2432 saw_syncer_event_ = false;
2433 }
2434
2435 // Original problem synopsis:
2436 // Illegal parent
2437 // Unexpected error during sync if we:
2438 // make a new folder bob
2439 // wait for sync
2440 // make a new folder fred
2441 // move bob into fred
2442 // remove bob
2443 // remove fred
2444 // if no syncing occured midway, bob will have an illegal parent
2445 TEST_F(SyncerTest, DeletingEntryInFolder) {
2446 // This test is a little fake.
2447 int64 existing_metahandle;
2448 {
2449 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2450 MutableEntry entry(&trans, CREATE, trans.root_id(), "existing");
2451 ASSERT_TRUE(entry.good());
2452 entry.Put(IS_DIR, true);
2453 entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
2454 entry.Put(IS_UNSYNCED, true);
2455 existing_metahandle = entry.Get(META_HANDLE);
2456 }
2457 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
2458 {
2459 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2460 MutableEntry newfolder(&trans, CREATE, trans.root_id(), "new");
2461 ASSERT_TRUE(newfolder.good());
2462 newfolder.Put(IS_DIR, true);
2463 newfolder.Put(SPECIFICS, DefaultBookmarkSpecifics());
2464 newfolder.Put(IS_UNSYNCED, true);
2465
2466 MutableEntry existing(&trans, GET_BY_HANDLE, existing_metahandle);
2467 ASSERT_TRUE(existing.good());
2468 existing.Put(PARENT_ID, newfolder.Get(ID));
2469 existing.Put(IS_UNSYNCED, true);
2470 EXPECT_TRUE(existing.Get(ID).ServerKnows());
2471
2472 newfolder.Put(IS_DEL, true);
2473 existing.Put(IS_DEL, true);
2474 }
2475 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
2476 const StatusController& status(session_->status_controller());
2477 EXPECT_EQ(0, status.TotalNumServerConflictingItems());
2478 }
2479
2480 TEST_F(SyncerTest, DeletingEntryWithLocalEdits) {
2481 int64 newfolder_metahandle;
2482
2483 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10);
2484 SyncShareAsDelegate();
2485 {
2486 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2487 MutableEntry newfolder(&trans, CREATE, ids_.FromNumber(1), "local");
2488 ASSERT_TRUE(newfolder.good());
2489 newfolder.Put(IS_UNSYNCED, true);
2490 newfolder.Put(IS_DIR, true);
2491 newfolder.Put(SPECIFICS, DefaultBookmarkSpecifics());
2492 newfolder_metahandle = newfolder.Get(META_HANDLE);
2493 }
2494 mock_server_->AddUpdateDirectory(1, 0, "bob", 2, 20);
2495 mock_server_->SetLastUpdateDeleted();
2496 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, APPLY_UPDATES);
2497 {
2498 ReadTransaction trans(FROM_HERE, directory());
2499 Entry entry(&trans, syncable::GET_BY_HANDLE, newfolder_metahandle);
2500 ASSERT_TRUE(entry.good());
2501 }
2502 }
2503
2504 TEST_F(SyncerTest, FolderSwapUpdate) {
2505 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10);
2506 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10);
2507 SyncShareAsDelegate();
2508 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20);
2509 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20);
2510 SyncShareAsDelegate();
2511 {
2512 ReadTransaction trans(FROM_HERE, directory());
2513 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2514 ASSERT_TRUE(id1.good());
2515 EXPECT_TRUE("fred" == id1.Get(NON_UNIQUE_NAME));
2516 EXPECT_TRUE(root_id_ == id1.Get(PARENT_ID));
2517 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2518 ASSERT_TRUE(id2.good());
2519 EXPECT_TRUE("bob" == id2.Get(NON_UNIQUE_NAME));
2520 EXPECT_TRUE(root_id_ == id2.Get(PARENT_ID));
2521 }
2522 saw_syncer_event_ = false;
2523 }
2524
2525 TEST_F(SyncerTest, NameCollidingFolderSwapWorksFine) {
2526 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10);
2527 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10);
2528 mock_server_->AddUpdateDirectory(4096, 0, "alice", 1, 10);
2529 SyncShareAsDelegate();
2530 {
2531 ReadTransaction trans(FROM_HERE, directory());
2532 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2533 ASSERT_TRUE(id1.good());
2534 EXPECT_TRUE("bob" == id1.Get(NON_UNIQUE_NAME));
2535 EXPECT_TRUE(root_id_ == id1.Get(PARENT_ID));
2536 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2537 ASSERT_TRUE(id2.good());
2538 EXPECT_TRUE("fred" == id2.Get(NON_UNIQUE_NAME));
2539 EXPECT_TRUE(root_id_ == id2.Get(PARENT_ID));
2540 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2541 ASSERT_TRUE(id3.good());
2542 EXPECT_TRUE("alice" == id3.Get(NON_UNIQUE_NAME));
2543 EXPECT_TRUE(root_id_ == id3.Get(PARENT_ID));
2544 }
2545 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20);
2546 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20);
2547 mock_server_->AddUpdateDirectory(4096, 0, "bob", 2, 20);
2548 SyncShareAsDelegate();
2549 {
2550 ReadTransaction trans(FROM_HERE, directory());
2551 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801));
2552 ASSERT_TRUE(id1.good());
2553 EXPECT_TRUE("fred" == id1.Get(NON_UNIQUE_NAME));
2554 EXPECT_TRUE(root_id_ == id1.Get(PARENT_ID));
2555 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024));
2556 ASSERT_TRUE(id2.good());
2557 EXPECT_TRUE("bob" == id2.Get(NON_UNIQUE_NAME));
2558 EXPECT_TRUE(root_id_ == id2.Get(PARENT_ID));
2559 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096));
2560 ASSERT_TRUE(id3.good());
2561 EXPECT_TRUE("bob" == id3.Get(NON_UNIQUE_NAME));
2562 EXPECT_TRUE(root_id_ == id3.Get(PARENT_ID));
2563 }
2564 saw_syncer_event_ = false;
2565 }
2566
2567 TEST_F(SyncerTest, CommitManyItemsInOneGo) {
2568 uint32 max_batches = 3;
2569 uint32 items_to_commit = kDefaultMaxCommitBatchSize * max_batches;
2570 {
2571 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2572 for (uint32 i = 0; i < items_to_commit; i++) {
2573 string nameutf8 = base::StringPrintf("%d", i);
2574 string name(nameutf8.begin(), nameutf8.end());
2575 MutableEntry e(&trans, CREATE, trans.root_id(), name);
2576 e.Put(IS_UNSYNCED, true);
2577 e.Put(IS_DIR, true);
2578 e.Put(SPECIFICS, DefaultBookmarkSpecifics());
2579 }
2580 }
2581 uint32 num_loops = 0;
2582 while (SyncShareAsDelegate()) {
2583 num_loops++;
2584 ASSERT_LT(num_loops, max_batches * 2);
2585 }
2586 EXPECT_GE(mock_server_->commit_messages().size(), max_batches);
2587 }
2588
2589 TEST_F(SyncerTest, HugeConflict) {
2590 int item_count = 300; // We should be able to do 300 or 3000 w/o issue.
2591
2592 syncable::Id parent_id = ids_.NewServerId();
2593 syncable::Id last_id = parent_id;
2594 vector<syncable::Id> tree_ids;
2595
2596 // Create a lot of updates for which the parent does not exist yet.
2597 // Generate a huge deep tree which should all fail to apply at first.
2598 {
2599 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2600 for (int i = 0; i < item_count ; i++) {
2601 syncable::Id next_id = ids_.NewServerId();
2602 tree_ids.push_back(next_id);
2603 mock_server_->AddUpdateDirectory(next_id, last_id, "BOB", 2, 20);
2604 last_id = next_id;
2605 }
2606 }
2607 SyncShareAsDelegate();
2608
2609 // Check they're in the expected conflict state.
2610 {
2611 ReadTransaction trans(FROM_HERE, directory());
2612 for (int i = 0; i < item_count; i++) {
2613 Entry e(&trans, GET_BY_ID, tree_ids[i]);
2614 // They should all exist but none should be applied.
2615 ASSERT_TRUE(e.good());
2616 EXPECT_TRUE(e.Get(IS_DEL));
2617 EXPECT_TRUE(e.Get(IS_UNAPPLIED_UPDATE));
2618 }
2619 }
2620
2621 // Add the missing parent directory.
2622 mock_server_->AddUpdateDirectory(parent_id, TestIdFactory::root(),
2623 "BOB", 2, 20);
2624 SyncShareAsDelegate();
2625
2626 // Now they should all be OK.
2627 {
2628 ReadTransaction trans(FROM_HERE, directory());
2629 for (int i = 0; i < item_count; i++) {
2630 Entry e(&trans, GET_BY_ID, tree_ids[i]);
2631 ASSERT_TRUE(e.good());
2632 EXPECT_FALSE(e.Get(IS_DEL));
2633 EXPECT_FALSE(e.Get(IS_UNAPPLIED_UPDATE));
2634 }
2635 }
2636 }
2637
2638 TEST_F(SyncerTest, DontCrashOnCaseChange) {
2639 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10);
2640 SyncShareAsDelegate();
2641 {
2642 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2643 MutableEntry e(&trans, GET_BY_ID, ids_.FromNumber(1));
2644 ASSERT_TRUE(e.good());
2645 e.Put(IS_UNSYNCED, true);
2646 }
2647 mock_server_->set_conflict_all_commits(true);
2648 mock_server_->AddUpdateDirectory(1, 0, "BOB", 2, 20);
2649 SyncShareAsDelegate(); // USED TO CAUSE AN ASSERT
2650 saw_syncer_event_ = false;
2651 }
2652
2653 TEST_F(SyncerTest, UnsyncedItemAndUpdate) {
2654 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10);
2655 SyncShareAsDelegate();
2656 mock_server_->set_conflict_all_commits(true);
2657 mock_server_->AddUpdateDirectory(2, 0, "bob", 2, 20);
2658 SyncShareAsDelegate(); // USED TO CAUSE AN ASSERT
2659 saw_syncer_event_ = false;
2660 }
2661
2662 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath) {
2663 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10);
2664 SyncShareAsDelegate();
2665 int64 local_folder_handle;
2666 syncable::Id local_folder_id;
2667 {
2668 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2669 MutableEntry new_entry(&wtrans, CREATE, wtrans.root_id(), "Bar.htm");
2670 ASSERT_TRUE(new_entry.good());
2671 local_folder_id = new_entry.Get(ID);
2672 local_folder_handle = new_entry.Get(META_HANDLE);
2673 new_entry.Put(IS_UNSYNCED, true);
2674 new_entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
2675 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2676 ASSERT_TRUE(old.good());
2677 WriteTestDataToEntry(&wtrans, &old);
2678 }
2679 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20);
2680 mock_server_->set_conflict_all_commits(true);
2681 SyncShareAsDelegate();
2682 saw_syncer_event_ = false;
2683 {
2684 // Update #20 should have been dropped in favor of the local version.
2685 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2686 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2687 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2688 ASSERT_TRUE(server.good());
2689 ASSERT_TRUE(local.good());
2690 EXPECT_TRUE(local.Get(META_HANDLE) != server.Get(META_HANDLE));
2691 EXPECT_FALSE(server.Get(IS_UNAPPLIED_UPDATE));
2692 EXPECT_FALSE(local.Get(IS_UNAPPLIED_UPDATE));
2693 EXPECT_TRUE(server.Get(IS_UNSYNCED));
2694 EXPECT_TRUE(local.Get(IS_UNSYNCED));
2695 EXPECT_EQ("Foo.htm", server.Get(NON_UNIQUE_NAME));
2696 EXPECT_EQ("Bar.htm", local.Get(NON_UNIQUE_NAME));
2697 }
2698 // Allow local changes to commit.
2699 mock_server_->set_conflict_all_commits(false);
2700 SyncShareAsDelegate();
2701 saw_syncer_event_ = false;
2702
2703 // Now add a server change to make the two names equal. There should
2704 // be no conflict with that, since names are not unique.
2705 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30);
2706 SyncShareAsDelegate();
2707 saw_syncer_event_ = false;
2708 {
2709 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2710 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2711 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2712 ASSERT_TRUE(server.good());
2713 ASSERT_TRUE(local.good());
2714 EXPECT_TRUE(local.Get(META_HANDLE) != server.Get(META_HANDLE));
2715 EXPECT_FALSE(server.Get(IS_UNAPPLIED_UPDATE));
2716 EXPECT_FALSE(local.Get(IS_UNAPPLIED_UPDATE));
2717 EXPECT_FALSE(server.Get(IS_UNSYNCED));
2718 EXPECT_FALSE(local.Get(IS_UNSYNCED));
2719 EXPECT_EQ("Bar.htm", server.Get(NON_UNIQUE_NAME));
2720 EXPECT_EQ("Bar.htm", local.Get(NON_UNIQUE_NAME));
2721 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2722 server.Get(SPECIFICS).bookmark().url());
2723 }
2724 }
2725
2726 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol.
2727 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto) {
2728 mock_server_->set_use_legacy_bookmarks_protocol(true);
2729 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10);
2730 SyncShareAsDelegate();
2731 int64 local_folder_handle;
2732 syncable::Id local_folder_id;
2733 {
2734 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2735 MutableEntry new_entry(&wtrans, CREATE, wtrans.root_id(), "Bar.htm");
2736 ASSERT_TRUE(new_entry.good());
2737 local_folder_id = new_entry.Get(ID);
2738 local_folder_handle = new_entry.Get(META_HANDLE);
2739 new_entry.Put(IS_UNSYNCED, true);
2740 new_entry.Put(SPECIFICS, DefaultBookmarkSpecifics());
2741 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2742 ASSERT_TRUE(old.good());
2743 WriteTestDataToEntry(&wtrans, &old);
2744 }
2745 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20);
2746 mock_server_->set_conflict_all_commits(true);
2747 SyncShareAsDelegate();
2748 saw_syncer_event_ = false;
2749 {
2750 // Update #20 should have been dropped in favor of the local version.
2751 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2752 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2753 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2754 ASSERT_TRUE(server.good());
2755 ASSERT_TRUE(local.good());
2756 EXPECT_TRUE(local.Get(META_HANDLE) != server.Get(META_HANDLE));
2757 EXPECT_FALSE(server.Get(IS_UNAPPLIED_UPDATE));
2758 EXPECT_FALSE(local.Get(IS_UNAPPLIED_UPDATE));
2759 EXPECT_TRUE(server.Get(IS_UNSYNCED));
2760 EXPECT_TRUE(local.Get(IS_UNSYNCED));
2761 EXPECT_EQ("Foo.htm", server.Get(NON_UNIQUE_NAME));
2762 EXPECT_EQ("Bar.htm", local.Get(NON_UNIQUE_NAME));
2763 }
2764 // Allow local changes to commit.
2765 mock_server_->set_conflict_all_commits(false);
2766 SyncShareAsDelegate();
2767 saw_syncer_event_ = false;
2768
2769 // Now add a server change to make the two names equal. There should
2770 // be no conflict with that, since names are not unique.
2771 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30);
2772 SyncShareAsDelegate();
2773 saw_syncer_event_ = false;
2774 {
2775 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2776 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2777 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle);
2778 ASSERT_TRUE(server.good());
2779 ASSERT_TRUE(local.good());
2780 EXPECT_TRUE(local.Get(META_HANDLE) != server.Get(META_HANDLE));
2781 EXPECT_FALSE(server.Get(IS_UNAPPLIED_UPDATE));
2782 EXPECT_FALSE(local.Get(IS_UNAPPLIED_UPDATE));
2783 EXPECT_FALSE(server.Get(IS_UNSYNCED));
2784 EXPECT_FALSE(local.Get(IS_UNSYNCED));
2785 EXPECT_EQ("Bar.htm", server.Get(NON_UNIQUE_NAME));
2786 EXPECT_EQ("Bar.htm", local.Get(NON_UNIQUE_NAME));
2787 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark.
2788 server.Get(SPECIFICS).bookmark().url());
2789 }
2790 }
2791
2792 // Circular links should be resolved by the server.
2793 TEST_F(SyncerTest, SiblingDirectoriesBecomeCircular) {
2794 // we don't currently resolve this. This test ensures we don't.
2795 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10);
2796 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10);
2797 SyncShareAsDelegate();
2798 {
2799 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2800 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2801 ASSERT_TRUE(A.good());
2802 A.Put(IS_UNSYNCED, true);
2803 ASSERT_TRUE(A.Put(PARENT_ID, ids_.FromNumber(2)));
2804 ASSERT_TRUE(A.Put(NON_UNIQUE_NAME, "B"));
2805 }
2806 mock_server_->AddUpdateDirectory(2, 1, "A", 20, 20);
2807 mock_server_->set_conflict_all_commits(true);
2808 SyncShareAsDelegate();
2809 saw_syncer_event_ = false;
2810 {
2811 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2812 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2813 ASSERT_TRUE(A.good());
2814 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2815 ASSERT_TRUE(B.good());
2816 EXPECT_TRUE(A.Get(NON_UNIQUE_NAME) == "B");
2817 EXPECT_TRUE(B.Get(NON_UNIQUE_NAME) == "B");
2818 }
2819 }
2820
2821 TEST_F(SyncerTest, SwapEntryNames) {
2822 // Simple transaction test.
2823 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10);
2824 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10);
2825 mock_server_->set_conflict_all_commits(true);
2826 SyncShareAsDelegate();
2827 {
2828 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
2829 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1));
2830 ASSERT_TRUE(A.good());
2831 A.Put(IS_UNSYNCED, true);
2832 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2));
2833 ASSERT_TRUE(B.good());
2834 B.Put(IS_UNSYNCED, true);
2835 ASSERT_TRUE(A.Put(NON_UNIQUE_NAME, "C"));
2836 ASSERT_TRUE(B.Put(NON_UNIQUE_NAME, "A"));
2837 ASSERT_TRUE(A.Put(NON_UNIQUE_NAME, "B"));
2838 }
2839 SyncShareAsDelegate();
2840 saw_syncer_event_ = false;
2841 }
2842
2843 TEST_F(SyncerTest, DualDeletionWithNewItemNameClash) {
2844 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10);
2845 mock_server_->AddUpdateBookmark(2, 0, "B", 10, 10);
2846 mock_server_->set_conflict_all_commits(true);
2847 SyncShareAsDelegate();
2848 {
2849 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2850 MutableEntry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2851 ASSERT_TRUE(B.good());
2852 WriteTestDataToEntry(&trans, &B);
2853 B.Put(IS_DEL, true);
2854 }
2855 mock_server_->AddUpdateBookmark(2, 0, "A", 11, 11);
2856 mock_server_->SetLastUpdateDeleted();
2857 SyncShareAsDelegate();
2858 {
2859 ReadTransaction trans(FROM_HERE, directory());
2860 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2));
2861 ASSERT_TRUE(B.good());
2862 EXPECT_FALSE(B.Get(IS_UNSYNCED));
2863 EXPECT_FALSE(B.Get(IS_UNAPPLIED_UPDATE));
2864 }
2865 saw_syncer_event_ = false;
2866 }
2867
2868 TEST_F(SyncerTest, ResolveWeWroteTheyDeleted) {
2869 int64 bob_metahandle;
2870
2871 mock_server_->AddUpdateBookmark(1, 0, "bob", 1, 10);
2872 SyncShareAsDelegate();
2873 {
2874 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2875 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
2876 ASSERT_TRUE(bob.good());
2877 bob_metahandle = bob.Get(META_HANDLE);
2878 WriteTestDataToEntry(&trans, &bob);
2879 }
2880 mock_server_->AddUpdateBookmark(1, 0, "bob", 2, 10);
2881 mock_server_->SetLastUpdateDeleted();
2882 mock_server_->set_conflict_all_commits(true);
2883 SyncShareAsDelegate();
2884 SyncShareAsDelegate();
2885 {
2886 ReadTransaction trans(FROM_HERE, directory());
2887 Entry bob(&trans, GET_BY_HANDLE, bob_metahandle);
2888 ASSERT_TRUE(bob.good());
2889 EXPECT_TRUE(bob.Get(IS_UNSYNCED));
2890 EXPECT_FALSE(bob.Get(ID).ServerKnows());
2891 EXPECT_FALSE(bob.Get(IS_UNAPPLIED_UPDATE));
2892 EXPECT_FALSE(bob.Get(IS_DEL));
2893 }
2894 saw_syncer_event_ = false;
2895 }
2896
2897 // This test is to reproduce a check failure. Sometimes we would get a bad ID
2898 // back when creating an entry.
2899 TEST_F(SyncerTest, DuplicateIDReturn) {
2900 {
2901 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2902 MutableEntry folder(&trans, CREATE, trans.root_id(), "bob");
2903 ASSERT_TRUE(folder.good());
2904 folder.Put(IS_UNSYNCED, true);
2905 folder.Put(IS_DIR, true);
2906 folder.Put(SPECIFICS, DefaultBookmarkSpecifics());
2907 MutableEntry folder2(&trans, CREATE, trans.root_id(), "fred");
2908 ASSERT_TRUE(folder2.good());
2909 folder2.Put(IS_UNSYNCED, false);
2910 folder2.Put(IS_DIR, true);
2911 folder2.Put(SPECIFICS, DefaultBookmarkSpecifics());
2912 folder2.Put(BASE_VERSION, 3);
2913 folder2.Put(ID, syncable::Id::CreateFromServerId("mock_server:10000"));
2914 }
2915 mock_server_->set_next_new_id(10000);
2916 EXPECT_EQ(1u, directory()->unsynced_entity_count());
2917 // we get back a bad id in here (should never happen).
2918 SyncShareAsDelegate();
2919 EXPECT_EQ(1u, directory()->unsynced_entity_count());
2920 SyncShareAsDelegate(); // another bad id in here.
2921 EXPECT_EQ(0u, directory()->unsynced_entity_count());
2922 saw_syncer_event_ = false;
2923 }
2924
2925 TEST_F(SyncerTest, DeletedEntryWithBadParentInLoopCalculation) {
2926 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10);
2927 SyncShareAsDelegate();
2928 {
2929 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2930 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1));
2931 ASSERT_TRUE(bob.good());
2932 // This is valid, because the parent could have gone away a long time ago.
2933 bob.Put(PARENT_ID, ids_.FromNumber(54));
2934 bob.Put(IS_DEL, true);
2935 bob.Put(IS_UNSYNCED, true);
2936 }
2937 mock_server_->AddUpdateDirectory(2, 1, "fred", 1, 10);
2938 SyncShareAsDelegate();
2939 SyncShareAsDelegate();
2940 }
2941
2942 TEST_F(SyncerTest, ConflictResolverMergesLocalDeleteAndServerUpdate) {
2943 {
2944 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2945
2946 MutableEntry local_deleted(&trans, CREATE, trans.root_id(), "name");
2947 local_deleted.Put(ID, ids_.FromNumber(1));
2948 local_deleted.Put(BASE_VERSION, 1);
2949 local_deleted.Put(IS_DEL, true);
2950 local_deleted.Put(IS_DIR, false);
2951 local_deleted.Put(IS_UNSYNCED, true);
2952 local_deleted.Put(SPECIFICS, DefaultBookmarkSpecifics());
2953 }
2954
2955 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10);
2956
2957 // We don't care about actually committing, just the resolution.
2958 mock_server_->set_conflict_all_commits(true);
2959 SyncShareAsDelegate();
2960
2961 {
2962 ReadTransaction trans(FROM_HERE, directory());
2963 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
2964 EXPECT_TRUE(local_deleted.Get(BASE_VERSION) == 10);
2965 EXPECT_TRUE(local_deleted.Get(IS_UNAPPLIED_UPDATE) == false);
2966 EXPECT_TRUE(local_deleted.Get(IS_UNSYNCED) == true);
2967 EXPECT_TRUE(local_deleted.Get(IS_DEL) == true);
2968 EXPECT_TRUE(local_deleted.Get(IS_DIR) == false);
2969 }
2970 }
2971
2972 // See what happens if the IS_DIR bit gets flipped. This can cause us
2973 // all kinds of disasters.
2974 TEST_F(SyncerTest, UpdateFlipsTheFolderBit) {
2975 // Local object: a deleted directory (container), revision 1, unsynced.
2976 {
2977 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
2978
2979 MutableEntry local_deleted(&trans, CREATE, trans.root_id(), "name");
2980 local_deleted.Put(ID, ids_.FromNumber(1));
2981 local_deleted.Put(BASE_VERSION, 1);
2982 local_deleted.Put(IS_DEL, true);
2983 local_deleted.Put(IS_DIR, true);
2984 local_deleted.Put(IS_UNSYNCED, true);
2985 local_deleted.Put(SPECIFICS, DefaultBookmarkSpecifics());
2986 }
2987
2988 // Server update: entry-type object (not a container), revision 10.
2989 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10);
2990
2991 // Don't attempt to commit.
2992 mock_server_->set_conflict_all_commits(true);
2993
2994 // The syncer should not attempt to apply the invalid update.
2995 SyncShareAsDelegate();
2996
2997 {
2998 ReadTransaction trans(FROM_HERE, directory());
2999 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1));
3000 EXPECT_TRUE(local_deleted.Get(BASE_VERSION) == 1);
3001 EXPECT_TRUE(local_deleted.Get(IS_UNAPPLIED_UPDATE) == false);
3002 EXPECT_TRUE(local_deleted.Get(IS_UNSYNCED) == true);
3003 EXPECT_TRUE(local_deleted.Get(IS_DEL) == true);
3004 EXPECT_TRUE(local_deleted.Get(IS_DIR) == true);
3005 }
3006 }
3007
3008 // Bug Synopsis:
3009 // Merge conflict resolution will merge a new local entry with another entry
3010 // that needs updates, resulting in CHECK.
3011 TEST_F(SyncerTest, MergingExistingItems) {
3012 mock_server_->set_conflict_all_commits(true);
3013 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10);
3014 SyncShareAsDelegate();
3015 {
3016 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3017 MutableEntry entry(&trans, CREATE, trans.root_id(), "Copy of base");
3018 WriteTestDataToEntry(&trans, &entry);
3019 }
3020 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50);
3021 SyncRepeatedlyToTriggerConflictResolution(session_.get());
3022 }
3023
3024 // In this test a long changelog contains a child at the start of the changelog
3025 // and a parent at the end. While these updates are in progress the client would
3026 // appear stuck.
3027 TEST_F(SyncerTest, LongChangelistWithApplicationConflict) {
3028 const int depth = 400;
3029 syncable::Id folder_id = ids_.FromNumber(1);
3030
3031 // First we an item in a folder in the root. However the folder won't come
3032 // till much later.
3033 syncable::Id stuck_entry_id = TestIdFactory::FromNumber(99999);
3034 mock_server_->AddUpdateDirectory(stuck_entry_id,
3035 folder_id, "stuck", 1, 1);
3036 mock_server_->SetChangesRemaining(depth - 1);
3037 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
3038
3039 // Buffer up a very long series of downloads.
3040 // We should never be stuck (conflict resolution shouldn't
3041 // kick in so long as we're making forward progress).
3042 for (int i = 0; i < depth; i++) {
3043 mock_server_->NextUpdateBatch();
3044 mock_server_->SetNewTimestamp(i + 1);
3045 mock_server_->SetChangesRemaining(depth - i);
3046 }
3047
3048 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END);
3049
3050 // Ensure our folder hasn't somehow applied.
3051 {
3052 ReadTransaction trans(FROM_HERE, directory());
3053 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3054 EXPECT_TRUE(child.good());
3055 EXPECT_TRUE(child.Get(IS_UNAPPLIED_UPDATE));
3056 EXPECT_TRUE(child.Get(IS_DEL));
3057 EXPECT_FALSE(child.Get(IS_UNSYNCED));
3058 }
3059
3060 // And finally the folder.
3061 mock_server_->AddUpdateDirectory(folder_id,
3062 TestIdFactory::root(), "folder", 1, 1);
3063 mock_server_->SetChangesRemaining(0);
3064 LoopSyncShare();
3065 LoopSyncShare();
3066 // Check that everything is as expected after the commit.
3067 {
3068 ReadTransaction trans(FROM_HERE, directory());
3069 Entry entry(&trans, GET_BY_ID, folder_id);
3070 ASSERT_TRUE(entry.good());
3071 Entry child(&trans, GET_BY_ID, stuck_entry_id);
3072 EXPECT_EQ(entry.Get(ID), child.Get(PARENT_ID));
3073 EXPECT_EQ("stuck", child.Get(NON_UNIQUE_NAME));
3074 EXPECT_TRUE(child.good());
3075 }
3076 }
3077
3078 TEST_F(SyncerTest, DontMergeTwoExistingItems) {
3079 mock_server_->set_conflict_all_commits(true);
3080 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10);
3081 mock_server_->AddUpdateBookmark(2, 0, "base2", 10, 10);
3082 SyncShareAsDelegate();
3083 {
3084 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3085 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3086 ASSERT_TRUE(entry.good());
3087 EXPECT_TRUE(entry.Put(NON_UNIQUE_NAME, "Copy of base"));
3088 entry.Put(IS_UNSYNCED, true);
3089 }
3090 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50);
3091 SyncRepeatedlyToTriggerConflictResolution(session_.get());
3092 {
3093 ReadTransaction trans(FROM_HERE, directory());
3094 Entry entry1(&trans, GET_BY_ID, ids_.FromNumber(1));
3095 EXPECT_FALSE(entry1.Get(IS_UNAPPLIED_UPDATE));
3096 EXPECT_FALSE(entry1.Get(IS_UNSYNCED));
3097 EXPECT_FALSE(entry1.Get(IS_DEL));
3098 Entry entry2(&trans, GET_BY_ID, ids_.FromNumber(2));
3099 EXPECT_FALSE(entry2.Get(IS_UNAPPLIED_UPDATE));
3100 EXPECT_TRUE(entry2.Get(IS_UNSYNCED));
3101 EXPECT_FALSE(entry2.Get(IS_DEL));
3102 EXPECT_EQ(entry1.Get(NON_UNIQUE_NAME), entry2.Get(NON_UNIQUE_NAME));
3103 }
3104 }
3105
3106 TEST_F(SyncerTest, TestUndeleteUpdate) {
3107 mock_server_->set_conflict_all_commits(true);
3108 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1);
3109 mock_server_->AddUpdateDirectory(2, 1, "bar", 1, 2);
3110 SyncShareAsDelegate();
3111 mock_server_->AddUpdateDirectory(2, 1, "bar", 2, 3);
3112 mock_server_->SetLastUpdateDeleted();
3113 SyncShareAsDelegate();
3114
3115 int64 metahandle;
3116 {
3117 ReadTransaction trans(FROM_HERE, directory());
3118 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3119 ASSERT_TRUE(entry.good());
3120 EXPECT_TRUE(entry.Get(IS_DEL));
3121 metahandle = entry.Get(META_HANDLE);
3122 }
3123 mock_server_->AddUpdateDirectory(1, 0, "foo", 2, 4);
3124 mock_server_->SetLastUpdateDeleted();
3125 SyncShareAsDelegate();
3126 // This used to be rejected as it's an undeletion. Now, it results in moving
3127 // the delete path aside.
3128 mock_server_->AddUpdateDirectory(2, 1, "bar", 3, 5);
3129 SyncShareAsDelegate();
3130 {
3131 ReadTransaction trans(FROM_HERE, directory());
3132 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3133 ASSERT_TRUE(entry.good());
3134 EXPECT_TRUE(entry.Get(IS_DEL));
3135 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3136 EXPECT_TRUE(entry.Get(IS_UNAPPLIED_UPDATE));
3137 EXPECT_NE(entry.Get(META_HANDLE), metahandle);
3138 }
3139 }
3140
3141 TEST_F(SyncerTest, TestMoveSanitizedNamedFolder) {
3142 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1);
3143 mock_server_->AddUpdateDirectory(2, 0, ":::", 1, 2);
3144 SyncShareAsDelegate();
3145 {
3146 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3147 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2));
3148 ASSERT_TRUE(entry.good());
3149 EXPECT_TRUE(entry.Put(PARENT_ID, ids_.FromNumber(1)));
3150 EXPECT_TRUE(entry.Put(IS_UNSYNCED, true));
3151 }
3152 SyncShareAsDelegate();
3153 // We use the same sync ts as before so our times match up.
3154 mock_server_->AddUpdateDirectory(2, 1, ":::", 2, 2);
3155 SyncShareAsDelegate();
3156 }
3157
3158 // Don't crash when this occurs.
3159 TEST_F(SyncerTest, UpdateWhereParentIsNotAFolder) {
3160 mock_server_->AddUpdateBookmark(1, 0, "B", 10, 10);
3161 mock_server_->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10);
3162 // Used to cause a CHECK
3163 SyncShareAsDelegate();
3164 {
3165 ReadTransaction rtrans(FROM_HERE, directory());
3166 Entry good_entry(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1));
3167 ASSERT_TRUE(good_entry.good());
3168 EXPECT_FALSE(good_entry.Get(IS_UNAPPLIED_UPDATE));
3169 Entry bad_parent(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2));
3170 ASSERT_TRUE(bad_parent.good());
3171 EXPECT_TRUE(bad_parent.Get(IS_UNAPPLIED_UPDATE));
3172 }
3173 }
3174
3175 const char kRootId[] = "0";
3176
3177 TEST_F(SyncerTest, DirectoryUpdateTest) {
3178 Id in_root_id = ids_.NewServerId();
3179 Id in_in_root_id = ids_.NewServerId();
3180
3181 mock_server_->AddUpdateDirectory(in_root_id, TestIdFactory::root(),
3182 "in_root_name", 2, 2);
3183 mock_server_->AddUpdateDirectory(in_in_root_id, in_root_id,
3184 "in_in_root_name", 3, 3);
3185 SyncShareAsDelegate();
3186 {
3187 ReadTransaction trans(FROM_HERE, directory());
3188 Entry in_root(&trans, GET_BY_ID, in_root_id);
3189 ASSERT_TRUE(in_root.good());
3190 EXPECT_EQ("in_root_name", in_root.Get(NON_UNIQUE_NAME));
3191 EXPECT_EQ(TestIdFactory::root(), in_root.Get(PARENT_ID));
3192
3193 Entry in_in_root(&trans, GET_BY_ID, in_in_root_id);
3194 ASSERT_TRUE(in_in_root.good());
3195 EXPECT_EQ("in_in_root_name", in_in_root.Get(NON_UNIQUE_NAME));
3196 EXPECT_EQ(in_root_id, in_in_root.Get(PARENT_ID));
3197 }
3198 }
3199
3200 TEST_F(SyncerTest, DirectoryCommitTest) {
3201 syncable::Id in_root_id, in_dir_id;
3202 int64 foo_metahandle;
3203 int64 bar_metahandle;
3204
3205 {
3206 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3207 MutableEntry parent(&wtrans, syncable::CREATE, root_id_, "foo");
3208 ASSERT_TRUE(parent.good());
3209 parent.Put(syncable::IS_UNSYNCED, true);
3210 parent.Put(syncable::IS_DIR, true);
3211 parent.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
3212 in_root_id = parent.Get(syncable::ID);
3213 foo_metahandle = parent.Get(META_HANDLE);
3214
3215 MutableEntry child(&wtrans, syncable::CREATE, parent.Get(ID), "bar");
3216 ASSERT_TRUE(child.good());
3217 child.Put(syncable::IS_UNSYNCED, true);
3218 child.Put(syncable::IS_DIR, true);
3219 child.Put(syncable::SPECIFICS, DefaultBookmarkSpecifics());
3220 bar_metahandle = child.Get(META_HANDLE);
3221 in_dir_id = parent.Get(syncable::ID);
3222 }
3223 SyncShareAsDelegate();
3224 {
3225 ReadTransaction trans(FROM_HERE, directory());
3226 Entry fail_by_old_id_entry(&trans, GET_BY_ID, in_root_id);
3227 ASSERT_FALSE(fail_by_old_id_entry.good());
3228
3229 Entry foo_entry(&trans, GET_BY_HANDLE, foo_metahandle);
3230 ASSERT_TRUE(foo_entry.good());
3231 EXPECT_EQ("foo", foo_entry.Get(NON_UNIQUE_NAME));
3232 EXPECT_NE(foo_entry.Get(syncable::ID), in_root_id);
3233
3234 Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle);
3235 ASSERT_TRUE(bar_entry.good());
3236 EXPECT_EQ("bar", bar_entry.Get(NON_UNIQUE_NAME));
3237 EXPECT_NE(bar_entry.Get(syncable::ID), in_dir_id);
3238 EXPECT_EQ(foo_entry.Get(syncable::ID), bar_entry.Get(PARENT_ID));
3239 }
3240 }
3241
3242 TEST_F(SyncerTest, TestClientCommand) {
3243 using sync_pb::ClientCommand;
3244
3245 ClientCommand* command = mock_server_->GetNextClientCommand();
3246 command->set_set_sync_poll_interval(8);
3247 command->set_set_sync_long_poll_interval(800);
3248 command->set_sessions_commit_delay_seconds(3141);
3249 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1);
3250 SyncShareAsDelegate();
3251
3252 EXPECT_TRUE(TimeDelta::FromSeconds(8) ==
3253 last_short_poll_interval_received_);
3254 EXPECT_TRUE(TimeDelta::FromSeconds(800) ==
3255 last_long_poll_interval_received_);
3256 EXPECT_TRUE(TimeDelta::FromSeconds(3141) ==
3257 last_sessions_commit_delay_seconds_);
3258
3259 command = mock_server_->GetNextClientCommand();
3260 command->set_set_sync_poll_interval(180);
3261 command->set_set_sync_long_poll_interval(190);
3262 command->set_sessions_commit_delay_seconds(2718);
3263 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1);
3264 SyncShareAsDelegate();
3265
3266 EXPECT_TRUE(TimeDelta::FromSeconds(180) ==
3267 last_short_poll_interval_received_);
3268 EXPECT_TRUE(TimeDelta::FromSeconds(190) ==
3269 last_long_poll_interval_received_);
3270 EXPECT_TRUE(TimeDelta::FromSeconds(2718) ==
3271 last_sessions_commit_delay_seconds_);
3272 }
3273
3274 TEST_F(SyncerTest, EnsureWeSendUpOldParent) {
3275 syncable::Id folder_one_id = ids_.FromNumber(1);
3276 syncable::Id folder_two_id = ids_.FromNumber(2);
3277
3278 mock_server_->AddUpdateDirectory(folder_one_id, TestIdFactory::root(),
3279 "folder_one", 1, 1);
3280 mock_server_->AddUpdateDirectory(folder_two_id, TestIdFactory::root(),
3281 "folder_two", 1, 1);
3282 SyncShareAsDelegate();
3283 {
3284 // A moved entry should send an "old parent."
3285 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3286 MutableEntry entry(&trans, GET_BY_ID, folder_one_id);
3287 ASSERT_TRUE(entry.good());
3288 entry.Put(PARENT_ID, folder_two_id);
3289 entry.Put(IS_UNSYNCED, true);
3290 // A new entry should send no "old parent."
3291 MutableEntry create(&trans, CREATE, trans.root_id(), "new_folder");
3292 create.Put(IS_UNSYNCED, true);
3293 create.Put(SPECIFICS, DefaultBookmarkSpecifics());
3294 }
3295 SyncShareAsDelegate();
3296 const sync_pb::CommitMessage& commit = mock_server_->last_sent_commit();
3297 ASSERT_EQ(2, commit.entries_size());
3298 EXPECT_TRUE(commit.entries(0).parent_id_string() == "2");
3299 EXPECT_TRUE(commit.entries(0).old_parent_id() == "0");
3300 EXPECT_FALSE(commit.entries(1).has_old_parent_id());
3301 }
3302
3303 TEST_F(SyncerTest, Test64BitVersionSupport) {
3304 int64 really_big_int = std::numeric_limits<int64>::max() - 12;
3305 const string name("ringo's dang orang ran rings around my o-ring");
3306 int64 item_metahandle;
3307
3308 // Try writing max int64 to the version fields of a meta entry.
3309 {
3310 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3311 MutableEntry entry(&wtrans, syncable::CREATE, wtrans.root_id(), name);
3312 ASSERT_TRUE(entry.good());
3313 entry.Put(syncable::BASE_VERSION, really_big_int);
3314 entry.Put(syncable::SERVER_VERSION, really_big_int);
3315 entry.Put(syncable::ID, ids_.NewServerId());
3316 item_metahandle = entry.Get(META_HANDLE);
3317 }
3318 // Now read it back out and make sure the value is max int64.
3319 ReadTransaction rtrans(FROM_HERE, directory());
3320 Entry entry(&rtrans, syncable::GET_BY_HANDLE, item_metahandle);
3321 ASSERT_TRUE(entry.good());
3322 EXPECT_TRUE(really_big_int == entry.Get(syncable::BASE_VERSION));
3323 }
3324
3325 TEST_F(SyncerTest, TestSimpleUndelete) {
3326 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3327 mock_server_->set_conflict_all_commits(true);
3328 // Let there be an entry from the server.
3329 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10);
3330 SyncShareAsDelegate();
3331 // Check it out and delete it.
3332 {
3333 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3334 MutableEntry entry(&wtrans, GET_BY_ID, id);
3335 ASSERT_TRUE(entry.good());
3336 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3337 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3338 EXPECT_FALSE(entry.Get(IS_DEL));
3339 // Delete it locally.
3340 entry.Put(IS_DEL, true);
3341 }
3342 SyncShareAsDelegate();
3343 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3344 {
3345 ReadTransaction trans(FROM_HERE, directory());
3346 Entry entry(&trans, GET_BY_ID, id);
3347 ASSERT_TRUE(entry.good());
3348 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3349 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3350 EXPECT_TRUE(entry.Get(IS_DEL));
3351 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3352 }
3353 SyncShareAsDelegate();
3354 // Update from server confirming deletion.
3355 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 11);
3356 mock_server_->SetLastUpdateDeleted();
3357 SyncShareAsDelegate();
3358 // IS_DEL AND SERVER_IS_DEL now both true.
3359 {
3360 ReadTransaction trans(FROM_HERE, directory());
3361 Entry entry(&trans, GET_BY_ID, id);
3362 ASSERT_TRUE(entry.good());
3363 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3364 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3365 EXPECT_TRUE(entry.Get(IS_DEL));
3366 EXPECT_TRUE(entry.Get(SERVER_IS_DEL));
3367 }
3368 // Undelete from server.
3369 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12);
3370 SyncShareAsDelegate();
3371 // IS_DEL and SERVER_IS_DEL now both false.
3372 {
3373 ReadTransaction trans(FROM_HERE, directory());
3374 Entry entry(&trans, GET_BY_ID, id);
3375 ASSERT_TRUE(entry.good());
3376 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3377 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3378 EXPECT_FALSE(entry.Get(IS_DEL));
3379 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3380 }
3381 }
3382
3383 TEST_F(SyncerTest, TestUndeleteWithMissingDeleteUpdate) {
3384 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root();
3385 // Let there be a entry, from the server.
3386 mock_server_->set_conflict_all_commits(true);
3387 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10);
3388 SyncShareAsDelegate();
3389 // Check it out and delete it.
3390 {
3391 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory());
3392 MutableEntry entry(&wtrans, GET_BY_ID, id);
3393 ASSERT_TRUE(entry.good());
3394 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3395 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3396 EXPECT_FALSE(entry.Get(IS_DEL));
3397 // Delete it locally.
3398 entry.Put(IS_DEL, true);
3399 }
3400 SyncShareAsDelegate();
3401 // Confirm we see IS_DEL and not SERVER_IS_DEL.
3402 {
3403 ReadTransaction trans(FROM_HERE, directory());
3404 Entry entry(&trans, GET_BY_ID, id);
3405 ASSERT_TRUE(entry.good());
3406 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3407 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3408 EXPECT_TRUE(entry.Get(IS_DEL));
3409 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3410 }
3411 SyncShareAsDelegate();
3412 // Say we do not get an update from server confirming deletion. Undelete
3413 // from server
3414 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12);
3415 SyncShareAsDelegate();
3416 // IS_DEL and SERVER_IS_DEL now both false.
3417 {
3418 ReadTransaction trans(FROM_HERE, directory());
3419 Entry entry(&trans, GET_BY_ID, id);
3420 ASSERT_TRUE(entry.good());
3421 EXPECT_FALSE(entry.Get(IS_UNAPPLIED_UPDATE));
3422 EXPECT_FALSE(entry.Get(IS_UNSYNCED));
3423 EXPECT_FALSE(entry.Get(IS_DEL));
3424 EXPECT_FALSE(entry.Get(SERVER_IS_DEL));
3425 }
3426 }
3427
3428 TEST_F(SyncerTest, TestUndeleteIgnoreCorrectlyUnappliedUpdate) {
3429 Id id1 = ids_.MakeServer("first"), id2 = ids_.MakeServer("second");
3430 Id root = TestIdFactory::root();
3431 // Duplicate! expect path clashing!
3432 mock_server_->set_conflict_all_commits(true);
3433 mock_server_->AddUpdateBookmark(id1, root, "foo", 1, 10);
3434 mock_server_->AddUpdateBookmark(id2, root, "foo", 1, 10);
3435 SyncShareAsDelegate();
3436 mock_server_->AddUpdateBookmark(id2, root, "foo2", 2, 20);
3437 SyncShareAsDelegate(); // Now just don't explode.
3438 }
3439
3440 TEST_F(SyncerTest, ClientTagServerCreatedUpdatesWork) {
3441 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10);
3442 mock_server_->SetLastUpdateClientTag("permfolder");
3443
3444 SyncShareAsDelegate();
3445
3446 {
3447 ReadTransaction trans(FROM_HERE, directory());
3448 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3449 ASSERT_TRUE(perm_folder.good());
3450 EXPECT_FALSE(perm_folder.Get(IS_DEL));
3451 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3452 EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
3453 EXPECT_EQ(perm_folder.Get(UNIQUE_CLIENT_TAG), "permfolder");
3454 EXPECT_EQ(perm_folder.Get(NON_UNIQUE_NAME), "permitem1");
3455 }
3456
3457 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100);
3458 mock_server_->SetLastUpdateClientTag("permfolder");
3459 SyncShareAsDelegate();
3460
3461 {
3462 ReadTransaction trans(FROM_HERE, directory());
3463
3464 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3465 ASSERT_TRUE(perm_folder.good());
3466 EXPECT_FALSE(perm_folder.Get(IS_DEL));
3467 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3468 EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
3469 EXPECT_EQ(perm_folder.Get(UNIQUE_CLIENT_TAG), "permfolder");
3470 EXPECT_EQ(perm_folder.Get(NON_UNIQUE_NAME), "permitem_renamed");
3471 }
3472 }
3473
3474 TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) {
3475 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10);
3476 mock_server_->SetLastUpdateClientTag("permfolder");
3477
3478 SyncShareAsDelegate();
3479
3480 {
3481 ReadTransaction trans(FROM_HERE, directory());
3482 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3483 ASSERT_TRUE(perm_folder.good());
3484 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3485 EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
3486 EXPECT_EQ(perm_folder.Get(UNIQUE_CLIENT_TAG), "permfolder");
3487 EXPECT_TRUE(perm_folder.Get(NON_UNIQUE_NAME) == "permitem1");
3488 EXPECT_TRUE(perm_folder.Get(ID).ServerKnows());
3489 }
3490
3491 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100);
3492 mock_server_->SetLastUpdateClientTag("wrongtag");
3493 SyncShareAsDelegate();
3494
3495 {
3496 ReadTransaction trans(FROM_HERE, directory());
3497
3498 // This update is rejected because it has the same ID, but a
3499 // different tag than one that is already on the client.
3500 // The client has a ServerKnows ID, which cannot be overwritten.
3501 Entry rejected_update(&trans, GET_BY_CLIENT_TAG, "wrongtag");
3502 EXPECT_FALSE(rejected_update.good());
3503
3504 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder");
3505 ASSERT_TRUE(perm_folder.good());
3506 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3507 EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
3508 EXPECT_EQ(perm_folder.Get(NON_UNIQUE_NAME), "permitem1");
3509 }
3510 }
3511
3512 TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) {
3513 int64 original_metahandle = 0;
3514
3515 sync_pb::EntitySpecifics local_bookmark(DefaultBookmarkSpecifics());
3516 local_bookmark.mutable_bookmark()->set_url("http://foo/localsite");
3517 sync_pb::EntitySpecifics server_bookmark(DefaultBookmarkSpecifics());
3518 server_bookmark.mutable_bookmark()->set_url("http://bar/serversite");
3519
3520 {
3521 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3522 MutableEntry perm_folder(&trans, CREATE, ids_.root(), "clientname");
3523 ASSERT_TRUE(perm_folder.good());
3524 perm_folder.Put(UNIQUE_CLIENT_TAG, "clientperm");
3525 perm_folder.Put(SPECIFICS, local_bookmark);
3526 perm_folder.Put(IS_UNSYNCED, true);
3527 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3528 EXPECT_FALSE(perm_folder.Get(ID).ServerKnows());
3529 original_metahandle = perm_folder.Get(META_HANDLE);
3530 }
3531
3532 mock_server_->AddUpdateBookmark(1, 0, "permitem_renamed", 10, 100);
3533 mock_server_->SetLastUpdateClientTag("clientperm");
3534 mock_server_->GetMutableLastUpdate()->mutable_specifics()->
3535 CopyFrom(server_bookmark);
3536 mock_server_->set_conflict_all_commits(true);
3537
3538 SyncShareAsDelegate();
3539 // This should cause client tag reunion, preserving the metahandle.
3540 {
3541 ReadTransaction trans(FROM_HERE, directory());
3542
3543 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "clientperm");
3544 ASSERT_TRUE(perm_folder.good());
3545 EXPECT_FALSE(perm_folder.Get(IS_DEL));
3546 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3547 EXPECT_TRUE(perm_folder.Get(IS_UNSYNCED));
3548 EXPECT_EQ(10, perm_folder.Get(BASE_VERSION));
3549 // Entry should have been given the new ID while preserving the
3550 // metahandle; client should have won the conflict resolution.
3551 EXPECT_EQ(original_metahandle, perm_folder.Get(META_HANDLE));
3552 EXPECT_EQ("clientperm", perm_folder.Get(UNIQUE_CLIENT_TAG));
3553 EXPECT_EQ("clientname", perm_folder.Get(NON_UNIQUE_NAME));
3554 EXPECT_EQ(local_bookmark.SerializeAsString(),
3555 perm_folder.Get(SPECIFICS).SerializeAsString());
3556 EXPECT_TRUE(perm_folder.Get(ID).ServerKnows());
3557 }
3558
3559 mock_server_->set_conflict_all_commits(false);
3560 SyncShareAsDelegate();
3561
3562 // The resolved entry ought to commit cleanly.
3563 {
3564 ReadTransaction trans(FROM_HERE, directory());
3565
3566 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "clientperm");
3567 ASSERT_TRUE(perm_folder.good());
3568 EXPECT_FALSE(perm_folder.Get(IS_DEL));
3569 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3570 EXPECT_FALSE(perm_folder.Get(IS_UNSYNCED));
3571 EXPECT_TRUE(10 < perm_folder.Get(BASE_VERSION));
3572 // Entry should have been given the new ID while preserving the
3573 // metahandle; client should have won the conflict resolution.
3574 EXPECT_EQ(original_metahandle, perm_folder.Get(META_HANDLE));
3575 EXPECT_EQ("clientperm", perm_folder.Get(UNIQUE_CLIENT_TAG));
3576 EXPECT_EQ("clientname", perm_folder.Get(NON_UNIQUE_NAME));
3577 EXPECT_EQ(local_bookmark.SerializeAsString(),
3578 perm_folder.Get(SPECIFICS).SerializeAsString());
3579 EXPECT_TRUE(perm_folder.Get(ID).ServerKnows());
3580 }
3581 }
3582
3583 TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) {
3584 {
3585 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3586 MutableEntry perm_folder(&trans, CREATE, ids_.root(), "clientname");
3587 ASSERT_TRUE(perm_folder.good());
3588 ASSERT_FALSE(perm_folder.Get(ID).ServerKnows());
3589 perm_folder.Put(UNIQUE_CLIENT_TAG, "clientperm");
3590 perm_folder.Put(SPECIFICS, DefaultBookmarkSpecifics());
3591 perm_folder.Put(IS_UNSYNCED, true);
3592 perm_folder.Put(IS_DEL, true);
3593 }
3594
3595 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100);
3596 mock_server_->SetLastUpdateClientTag("clientperm");
3597 mock_server_->set_conflict_all_commits(true);
3598
3599 SyncShareAsDelegate();
3600 // This should cause client tag overwrite.
3601 {
3602 ReadTransaction trans(FROM_HERE, directory());
3603
3604 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "clientperm");
3605 ASSERT_TRUE(perm_folder.good());
3606 ASSERT_TRUE(perm_folder.Get(ID).ServerKnows());
3607 EXPECT_TRUE(perm_folder.Get(IS_DEL));
3608 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3609 EXPECT_TRUE(perm_folder.Get(IS_UNSYNCED));
3610 EXPECT_EQ(perm_folder.Get(BASE_VERSION), 10);
3611 EXPECT_EQ(perm_folder.Get(UNIQUE_CLIENT_TAG), "clientperm");
3612 }
3613 }
3614
3615 TEST_F(SyncerTest, ClientTagUpdateClashesWithLocalEntry) {
3616 // This test is written assuming that ID comparison
3617 // will work out in a particular way.
3618 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(2));
3619 EXPECT_TRUE(ids_.FromNumber(3) < ids_.FromNumber(4));
3620
3621 mock_server_->AddUpdateBookmark(1, 0, "One", 10, 100);
3622 mock_server_->SetLastUpdateClientTag("tag1");
3623 mock_server_->AddUpdateBookmark(4, 0, "Four", 11, 110);
3624 mock_server_->SetLastUpdateClientTag("tag2");
3625
3626 mock_server_->set_conflict_all_commits(true);
3627
3628 SyncShareAsDelegate();
3629 int64 tag1_metahandle = syncable::kInvalidMetaHandle;
3630 int64 tag2_metahandle = syncable::kInvalidMetaHandle;
3631 // This should cause client tag overwrite.
3632 {
3633 ReadTransaction trans(FROM_HERE, directory());
3634
3635 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3636 ASSERT_TRUE(tag1.good());
3637 ASSERT_TRUE(tag1.Get(ID).ServerKnows());
3638 ASSERT_TRUE(ids_.FromNumber(1) == tag1.Get(ID));
3639 EXPECT_FALSE(tag1.Get(IS_DEL));
3640 EXPECT_FALSE(tag1.Get(IS_UNAPPLIED_UPDATE));
3641 EXPECT_FALSE(tag1.Get(IS_UNSYNCED));
3642 EXPECT_EQ("One", tag1.Get(NON_UNIQUE_NAME));
3643 EXPECT_EQ(10, tag1.Get(BASE_VERSION));
3644 EXPECT_EQ("tag1", tag1.Get(UNIQUE_CLIENT_TAG));
3645 tag1_metahandle = tag1.Get(META_HANDLE);
3646
3647 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3648 ASSERT_TRUE(tag2.good());
3649 ASSERT_TRUE(tag2.Get(ID).ServerKnows());
3650 ASSERT_TRUE(ids_.FromNumber(4) == tag2.Get(ID));
3651 EXPECT_FALSE(tag2.Get(IS_DEL));
3652 EXPECT_FALSE(tag2.Get(IS_UNAPPLIED_UPDATE));
3653 EXPECT_FALSE(tag2.Get(IS_UNSYNCED));
3654 EXPECT_EQ("Four", tag2.Get(NON_UNIQUE_NAME));
3655 EXPECT_EQ(11, tag2.Get(BASE_VERSION));
3656 EXPECT_EQ("tag2", tag2.Get(UNIQUE_CLIENT_TAG));
3657 tag2_metahandle = tag2.Get(META_HANDLE);
3658
3659 syncable::Directory::ChildHandles children;
3660 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3661 ASSERT_EQ(2U, children.size());
3662 }
3663
3664 mock_server_->AddUpdateBookmark(2, 0, "Two", 12, 120);
3665 mock_server_->SetLastUpdateClientTag("tag1");
3666 mock_server_->AddUpdateBookmark(3, 0, "Three", 13, 130);
3667 mock_server_->SetLastUpdateClientTag("tag2");
3668 SyncShareAsDelegate();
3669
3670 {
3671 ReadTransaction trans(FROM_HERE, directory());
3672
3673 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1");
3674 ASSERT_TRUE(tag1.good());
3675 ASSERT_TRUE(tag1.Get(ID).ServerKnows());
3676 ASSERT_TRUE(ids_.FromNumber(1) == tag1.Get(ID))
3677 << "ID 1 should be kept, since it was less than ID 2.";
3678 EXPECT_FALSE(tag1.Get(IS_DEL));
3679 EXPECT_FALSE(tag1.Get(IS_UNAPPLIED_UPDATE));
3680 EXPECT_FALSE(tag1.Get(IS_UNSYNCED));
3681 EXPECT_EQ(10, tag1.Get(BASE_VERSION));
3682 EXPECT_EQ("tag1", tag1.Get(UNIQUE_CLIENT_TAG));
3683 EXPECT_EQ("One", tag1.Get(NON_UNIQUE_NAME));
3684 EXPECT_EQ(tag1_metahandle, tag1.Get(META_HANDLE));
3685
3686 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2");
3687 ASSERT_TRUE(tag2.good());
3688 ASSERT_TRUE(tag2.Get(ID).ServerKnows());
3689 ASSERT_TRUE(ids_.FromNumber(3) == tag2.Get(ID))
3690 << "ID 3 should be kept, since it was less than ID 4.";
3691 EXPECT_FALSE(tag2.Get(IS_DEL));
3692 EXPECT_FALSE(tag2.Get(IS_UNAPPLIED_UPDATE));
3693 EXPECT_FALSE(tag2.Get(IS_UNSYNCED));
3694 EXPECT_EQ("Three", tag2.Get(NON_UNIQUE_NAME));
3695 EXPECT_EQ(13, tag2.Get(BASE_VERSION));
3696 EXPECT_EQ("tag2", tag2.Get(UNIQUE_CLIENT_TAG));
3697 EXPECT_EQ(tag2_metahandle, tag2.Get(META_HANDLE));
3698
3699 syncable::Directory::ChildHandles children;
3700 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3701 ASSERT_EQ(2U, children.size());
3702 }
3703 }
3704
3705 TEST_F(SyncerTest, ClientTagClashWithinBatchOfUpdates) {
3706 // This test is written assuming that ID comparison
3707 // will work out in a particular way.
3708 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(4));
3709 EXPECT_TRUE(ids_.FromNumber(201) < ids_.FromNumber(205));
3710
3711 mock_server_->AddUpdateBookmark(1, 0, "One A", 1, 10);
3712 mock_server_->SetLastUpdateClientTag("tag a"); // Least ID: winner.
3713 mock_server_->AddUpdateBookmark(2, 0, "Two A", 11, 110);
3714 mock_server_->SetLastUpdateClientTag("tag a");
3715 mock_server_->AddUpdateBookmark(3, 0, "Three A", 12, 120);
3716 mock_server_->SetLastUpdateClientTag("tag a");
3717 mock_server_->AddUpdateBookmark(4, 0, "Four A", 13, 130);
3718 mock_server_->SetLastUpdateClientTag("tag a");
3719
3720 mock_server_->AddUpdateBookmark(105, 0, "One B", 14, 140);
3721 mock_server_->SetLastUpdateClientTag("tag b");
3722 mock_server_->AddUpdateBookmark(102, 0, "Two B", 15, 150);
3723 mock_server_->SetLastUpdateClientTag("tag b");
3724 mock_server_->AddUpdateBookmark(101, 0, "Three B", 16, 160);
3725 mock_server_->SetLastUpdateClientTag("tag b"); // Least ID: winner.
3726 mock_server_->AddUpdateBookmark(104, 0, "Four B", 17, 170);
3727 mock_server_->SetLastUpdateClientTag("tag b");
3728
3729 mock_server_->AddUpdateBookmark(205, 0, "One C", 18, 180);
3730 mock_server_->SetLastUpdateClientTag("tag c");
3731 mock_server_->AddUpdateBookmark(202, 0, "Two C", 19, 190);
3732 mock_server_->SetLastUpdateClientTag("tag c");
3733 mock_server_->AddUpdateBookmark(204, 0, "Three C", 20, 200);
3734 mock_server_->SetLastUpdateClientTag("tag c");
3735 mock_server_->AddUpdateBookmark(201, 0, "Four C", 21, 210);
3736 mock_server_->SetLastUpdateClientTag("tag c"); // Least ID: winner.
3737
3738 mock_server_->set_conflict_all_commits(true);
3739
3740 SyncShareAsDelegate();
3741 // This should cause client tag overwrite.
3742 {
3743 ReadTransaction trans(FROM_HERE, directory());
3744
3745 Entry tag_a(&trans, GET_BY_CLIENT_TAG, "tag a");
3746 ASSERT_TRUE(tag_a.good());
3747 EXPECT_TRUE(tag_a.Get(ID).ServerKnows());
3748 EXPECT_EQ(ids_.FromNumber(1), tag_a.Get(ID));
3749 EXPECT_FALSE(tag_a.Get(IS_DEL));
3750 EXPECT_FALSE(tag_a.Get(IS_UNAPPLIED_UPDATE));
3751 EXPECT_FALSE(tag_a.Get(IS_UNSYNCED));
3752 EXPECT_EQ("One A", tag_a.Get(NON_UNIQUE_NAME));
3753 EXPECT_EQ(1, tag_a.Get(BASE_VERSION));
3754 EXPECT_EQ("tag a", tag_a.Get(UNIQUE_CLIENT_TAG));
3755
3756 Entry tag_b(&trans, GET_BY_CLIENT_TAG, "tag b");
3757 ASSERT_TRUE(tag_b.good());
3758 EXPECT_TRUE(tag_b.Get(ID).ServerKnows());
3759 EXPECT_EQ(ids_.FromNumber(101), tag_b.Get(ID));
3760 EXPECT_FALSE(tag_b.Get(IS_DEL));
3761 EXPECT_FALSE(tag_b.Get(IS_UNAPPLIED_UPDATE));
3762 EXPECT_FALSE(tag_b.Get(IS_UNSYNCED));
3763 EXPECT_EQ("Three B", tag_b.Get(NON_UNIQUE_NAME));
3764 EXPECT_EQ(16, tag_b.Get(BASE_VERSION));
3765 EXPECT_EQ("tag b", tag_b.Get(UNIQUE_CLIENT_TAG));
3766
3767 Entry tag_c(&trans, GET_BY_CLIENT_TAG, "tag c");
3768 ASSERT_TRUE(tag_c.good());
3769 EXPECT_TRUE(tag_c.Get(ID).ServerKnows());
3770 EXPECT_EQ(ids_.FromNumber(201), tag_c.Get(ID));
3771 EXPECT_FALSE(tag_c.Get(IS_DEL));
3772 EXPECT_FALSE(tag_c.Get(IS_UNAPPLIED_UPDATE));
3773 EXPECT_FALSE(tag_c.Get(IS_UNSYNCED));
3774 EXPECT_EQ("Four C", tag_c.Get(NON_UNIQUE_NAME));
3775 EXPECT_EQ(21, tag_c.Get(BASE_VERSION));
3776 EXPECT_EQ("tag c", tag_c.Get(UNIQUE_CLIENT_TAG));
3777
3778 syncable::Directory::ChildHandles children;
3779 directory()->GetChildHandlesById(&trans, trans.root_id(), &children);
3780 ASSERT_EQ(3U, children.size());
3781 }
3782 }
3783
3784 TEST_F(SyncerTest, UniqueServerTagUpdates) {
3785 // As a hurdle, introduce an item whose name is the same as the tag value
3786 // we'll use later.
3787 int64 hurdle_handle = CreateUnsyncedDirectory("bob", "id_bob");
3788 {
3789 ReadTransaction trans(FROM_HERE, directory());
3790 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
3791 ASSERT_TRUE(hurdle.good());
3792 ASSERT_TRUE(!hurdle.Get(IS_DEL));
3793 ASSERT_TRUE(hurdle.Get(UNIQUE_SERVER_TAG).empty());
3794 ASSERT_TRUE(hurdle.Get(NON_UNIQUE_NAME) == "bob");
3795
3796 // Try to lookup by the tagname. These should fail.
3797 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
3798 EXPECT_FALSE(tag_alpha.good());
3799 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
3800 EXPECT_FALSE(tag_bob.good());
3801 }
3802
3803 // Now download some tagged items as updates.
3804 mock_server_->AddUpdateDirectory(1, 0, "update1", 1, 10);
3805 mock_server_->SetLastUpdateServerTag("alpha");
3806 mock_server_->AddUpdateDirectory(2, 0, "update2", 2, 20);
3807 mock_server_->SetLastUpdateServerTag("bob");
3808 SyncShareAsDelegate();
3809
3810 {
3811 ReadTransaction trans(FROM_HERE, directory());
3812
3813 // The new items should be applied as new entries, and we should be able
3814 // to look them up by their tag values.
3815 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha");
3816 ASSERT_TRUE(tag_alpha.good());
3817 ASSERT_TRUE(!tag_alpha.Get(IS_DEL));
3818 ASSERT_TRUE(tag_alpha.Get(UNIQUE_SERVER_TAG) == "alpha");
3819 ASSERT_TRUE(tag_alpha.Get(NON_UNIQUE_NAME) == "update1");
3820 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob");
3821 ASSERT_TRUE(tag_bob.good());
3822 ASSERT_TRUE(!tag_bob.Get(IS_DEL));
3823 ASSERT_TRUE(tag_bob.Get(UNIQUE_SERVER_TAG) == "bob");
3824 ASSERT_TRUE(tag_bob.Get(NON_UNIQUE_NAME) == "update2");
3825 // The old item should be unchanged.
3826 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle);
3827 ASSERT_TRUE(hurdle.good());
3828 ASSERT_TRUE(!hurdle.Get(IS_DEL));
3829 ASSERT_TRUE(hurdle.Get(UNIQUE_SERVER_TAG).empty());
3830 ASSERT_TRUE(hurdle.Get(NON_UNIQUE_NAME) == "bob");
3831 }
3832 }
3833
3834 TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) {
3835 // The expectations of this test happen in the MockConnectionManager's
3836 // GetUpdates handler. EnableDatatype sets the expectation value from our
3837 // set of enabled/disabled datatypes.
3838 EnableDatatype(syncable::BOOKMARKS);
3839 SyncShareAsDelegate();
3840 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3841
3842 EnableDatatype(syncable::AUTOFILL);
3843 SyncShareAsDelegate();
3844 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3845
3846 EnableDatatype(syncable::PREFERENCES);
3847 SyncShareAsDelegate();
3848 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3849
3850 DisableDatatype(syncable::BOOKMARKS);
3851 SyncShareAsDelegate();
3852 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3853
3854 DisableDatatype(syncable::AUTOFILL);
3855 SyncShareAsDelegate();
3856 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3857
3858 DisableDatatype(syncable::PREFERENCES);
3859 EnableDatatype(syncable::AUTOFILL);
3860 SyncShareAsDelegate();
3861 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
3862 }
3863
3864 // Test what happens if a client deletes, then recreates, an object very
3865 // quickly. It is possible that the deletion gets sent as a commit, and
3866 // the undelete happens during the commit request. The principle here
3867 // is that with a single committing client, conflicts should never
3868 // be encountered, and a client encountering its past actions during
3869 // getupdates should never feed back to override later actions.
3870 //
3871 // In cases of ordering A-F below, the outcome should be the same.
3872 // Exercised by UndeleteDuringCommit:
3873 // A. Delete - commit - undelete - commitresponse.
3874 // B. Delete - commit - undelete - commitresponse - getupdates.
3875 // Exercised by UndeleteBeforeCommit:
3876 // C. Delete - undelete - commit - commitresponse.
3877 // D. Delete - undelete - commit - commitresponse - getupdates.
3878 // Exercised by UndeleteAfterCommit:
3879 // E. Delete - commit - commitresponse - undelete - commit
3880 // - commitresponse.
3881 // F. Delete - commit - commitresponse - undelete - commit -
3882 // - commitresponse - getupdates.
3883 class SyncerUndeletionTest : public SyncerTest {
3884 public:
3885 SyncerUndeletionTest()
3886 : client_tag_("foobar"),
3887 metahandle_(syncable::kInvalidMetaHandle) {
3888 }
3889
3890 void Create() {
3891 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3892 MutableEntry perm_folder(&trans, CREATE, ids_.root(), "clientname");
3893 ASSERT_TRUE(perm_folder.good());
3894 perm_folder.Put(UNIQUE_CLIENT_TAG, client_tag_);
3895 perm_folder.Put(IS_UNSYNCED, true);
3896 perm_folder.Put(SYNCING, false);
3897 perm_folder.Put(SPECIFICS, DefaultBookmarkSpecifics());
3898 EXPECT_FALSE(perm_folder.Get(IS_UNAPPLIED_UPDATE));
3899 EXPECT_FALSE(perm_folder.Get(ID).ServerKnows());
3900 metahandle_ = perm_folder.Get(META_HANDLE);
3901 }
3902
3903 void Delete() {
3904 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3905 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
3906 ASSERT_TRUE(entry.good());
3907 EXPECT_EQ(metahandle_, entry.Get(META_HANDLE));
3908 entry.Put(IS_DEL, true);
3909 entry.Put(IS_UNSYNCED, true);
3910 entry.Put(SYNCING, false);
3911 }
3912
3913 void Undelete() {
3914 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
3915 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
3916 ASSERT_TRUE(entry.good());
3917 EXPECT_EQ(metahandle_, entry.Get(META_HANDLE));
3918 EXPECT_TRUE(entry.Get(IS_DEL));
3919 entry.Put(IS_DEL, false);
3920 entry.Put(IS_UNSYNCED, true);
3921 entry.Put(SYNCING, false);
3922 }
3923
3924 int64 GetMetahandleOfTag() {
3925 ReadTransaction trans(FROM_HERE, directory());
3926 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_);
3927 EXPECT_TRUE(entry.good());
3928 if (!entry.good()) {
3929 return syncable::kInvalidMetaHandle;
3930 }
3931 return entry.Get(META_HANDLE);
3932 }
3933
3934 void ExpectUnsyncedCreation() {
3935 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
3936 EXPECT_FALSE(Get(metahandle_, IS_DEL));
3937 EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL)); // Never been committed.
3938 EXPECT_GE(0, Get(metahandle_, BASE_VERSION));
3939 EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
3940 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
3941 }
3942
3943 void ExpectUnsyncedUndeletion() {
3944 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
3945 EXPECT_FALSE(Get(metahandle_, IS_DEL));
3946 EXPECT_TRUE(Get(metahandle_, SERVER_IS_DEL));
3947 EXPECT_EQ(0, Get(metahandle_, BASE_VERSION));
3948 EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
3949 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
3950 EXPECT_TRUE(Get(metahandle_, ID).ServerKnows());
3951 }
3952
3953 void ExpectUnsyncedEdit() {
3954 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
3955 EXPECT_FALSE(Get(metahandle_, IS_DEL));
3956 EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
3957 EXPECT_LT(0, Get(metahandle_, BASE_VERSION));
3958 EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
3959 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
3960 EXPECT_TRUE(Get(metahandle_, ID).ServerKnows());
3961 }
3962
3963 void ExpectUnsyncedDeletion() {
3964 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
3965 EXPECT_TRUE(Get(metahandle_, IS_DEL));
3966 EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
3967 EXPECT_TRUE(Get(metahandle_, IS_UNSYNCED));
3968 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
3969 EXPECT_LT(0, Get(metahandle_, BASE_VERSION));
3970 EXPECT_LT(0, Get(metahandle_, SERVER_VERSION));
3971 }
3972
3973 void ExpectSyncedAndCreated() {
3974 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
3975 EXPECT_FALSE(Get(metahandle_, IS_DEL));
3976 EXPECT_FALSE(Get(metahandle_, SERVER_IS_DEL));
3977 EXPECT_LT(0, Get(metahandle_, BASE_VERSION));
3978 EXPECT_EQ(Get(metahandle_, BASE_VERSION), Get(metahandle_, SERVER_VERSION));
3979 EXPECT_FALSE(Get(metahandle_, IS_UNSYNCED));
3980 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
3981 }
3982
3983 void ExpectSyncedAndDeleted() {
3984 EXPECT_EQ(metahandle_, GetMetahandleOfTag());
3985 EXPECT_TRUE(Get(metahandle_, IS_DEL));
3986 EXPECT_TRUE(Get(metahandle_, SERVER_IS_DEL));
3987 EXPECT_FALSE(Get(metahandle_, IS_UNSYNCED));
3988 EXPECT_FALSE(Get(metahandle_, IS_UNAPPLIED_UPDATE));
3989 EXPECT_GE(0, Get(metahandle_, BASE_VERSION));
3990 EXPECT_GE(0, Get(metahandle_, SERVER_VERSION));
3991 }
3992
3993 protected:
3994 const std::string client_tag_;
3995 int64 metahandle_;
3996 };
3997
3998 TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) {
3999 Create();
4000 ExpectUnsyncedCreation();
4001 SyncShareAsDelegate();
4002
4003 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4004 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4005 ExpectSyncedAndCreated();
4006
4007 // Delete, begin committing the delete, then undelete while committing.
4008 Delete();
4009 ExpectUnsyncedDeletion();
4010 mock_server_->SetMidCommitCallback(
4011 base::Bind(&SyncerUndeletionTest::Undelete, base::Unretained(this)));
4012 SyncShareAsDelegate();
4013
4014 // The item ought to exist as an unsynced undeletion (meaning,
4015 // we think that the next commit ought to be a recreation commit).
4016 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4017 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4018 ExpectUnsyncedUndeletion();
4019
4020 // Now, encounter a GetUpdates corresponding to the deletion from
4021 // the server. The undeletion should prevail again and be committed.
4022 // None of this should trigger any conflict detection -- it is perfectly
4023 // normal to recieve updates from our own commits.
4024 mock_server_->SetMidCommitCallback(base::Closure());
4025 mock_server_->AddUpdateTombstone(Get(metahandle_, ID));
4026 SyncShareAsDelegate();
4027 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4028 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4029 ExpectSyncedAndCreated();
4030 }
4031
4032 TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) {
4033 Create();
4034 ExpectUnsyncedCreation();
4035 SyncShareAsDelegate();
4036
4037 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4038 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4039 ExpectSyncedAndCreated();
4040
4041 // Delete and undelete, then sync to pick up the result.
4042 Delete();
4043 ExpectUnsyncedDeletion();
4044 Undelete();
4045 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists.
4046 SyncShareAsDelegate();
4047
4048 // The item ought to have committed successfully.
4049 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4050 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4051 ExpectSyncedAndCreated();
4052 EXPECT_EQ(2, Get(metahandle_, BASE_VERSION));
4053
4054 // Now, encounter a GetUpdates corresponding to the just-committed
4055 // update.
4056 mock_server_->AddUpdateFromLastCommit();
4057 SyncShareAsDelegate();
4058 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4059 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4060 ExpectSyncedAndCreated();
4061 }
4062
4063 TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) {
4064 Create();
4065 ExpectUnsyncedCreation();
4066 SyncShareAsDelegate();
4067
4068 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4069 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4070 ExpectSyncedAndCreated();
4071
4072 // Delete and commit.
4073 Delete();
4074 ExpectUnsyncedDeletion();
4075 SyncShareAsDelegate();
4076
4077 // The item ought to have committed successfully.
4078 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4079 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4080 ExpectSyncedAndDeleted();
4081
4082 // Before the GetUpdates, the item is locally undeleted.
4083 Undelete();
4084 ExpectUnsyncedUndeletion();
4085
4086 // Now, encounter a GetUpdates corresponding to the just-committed
4087 // deletion update. The undeletion should prevail.
4088 mock_server_->AddUpdateFromLastCommit();
4089 SyncShareAsDelegate();
4090 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4091 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4092 ExpectSyncedAndCreated();
4093 }
4094
4095 TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) {
4096 Create();
4097 ExpectUnsyncedCreation();
4098 SyncShareAsDelegate();
4099
4100 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4101 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4102 ExpectSyncedAndCreated();
4103
4104 mock_server_->AddUpdateFromLastCommit();
4105 SyncShareAsDelegate();
4106 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4107 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4108 ExpectSyncedAndCreated();
4109
4110 // Delete and commit.
4111 Delete();
4112 ExpectUnsyncedDeletion();
4113 SyncShareAsDelegate();
4114
4115 // The item ought to have committed successfully.
4116 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4117 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4118 ExpectSyncedAndDeleted();
4119
4120 // Now, encounter a GetUpdates corresponding to the just-committed
4121 // deletion update. Should be consistent.
4122 mock_server_->AddUpdateFromLastCommit();
4123 SyncShareAsDelegate();
4124 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4125 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4126 ExpectSyncedAndDeleted();
4127
4128 // After the GetUpdates, the item is locally undeleted.
4129 Undelete();
4130 ExpectUnsyncedUndeletion();
4131
4132 // Now, encounter a GetUpdates corresponding to the just-committed
4133 // deletion update. The undeletion should prevail.
4134 SyncShareAsDelegate();
4135 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4136 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4137 ExpectSyncedAndCreated();
4138 }
4139
4140 // Test processing of undeletion GetUpdateses.
4141 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) {
4142 Create();
4143 ExpectUnsyncedCreation();
4144 SyncShareAsDelegate();
4145
4146 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4147 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4148 ExpectSyncedAndCreated();
4149
4150 // Add a delete from the server.
4151 mock_server_->AddUpdateFromLastCommit();
4152 SyncShareAsDelegate();
4153 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4154 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4155 ExpectSyncedAndCreated();
4156
4157 // Some other client deletes the item.
4158 mock_server_->AddUpdateTombstone(Get(metahandle_, ID));
4159 SyncShareAsDelegate();
4160
4161 // The update ought to have applied successfully.
4162 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4163 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4164 ExpectSyncedAndDeleted();
4165
4166 // Undelete it locally.
4167 Undelete();
4168 ExpectUnsyncedUndeletion();
4169 SyncShareAsDelegate();
4170 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4171 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4172 ExpectSyncedAndCreated();
4173
4174 // Now, encounter a GetUpdates corresponding to the just-committed
4175 // deletion update. The undeletion should prevail.
4176 mock_server_->AddUpdateFromLastCommit();
4177 SyncShareAsDelegate();
4178 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4179 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4180 ExpectSyncedAndCreated();
4181 }
4182
4183 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) {
4184 Create();
4185 ExpectUnsyncedCreation();
4186 SyncShareAsDelegate();
4187
4188 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4189 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4190 ExpectSyncedAndCreated();
4191
4192 // Some other client deletes the item before we get a chance
4193 // to GetUpdates our original request.
4194 mock_server_->AddUpdateTombstone(Get(metahandle_, ID));
4195 SyncShareAsDelegate();
4196
4197 // The update ought to have applied successfully.
4198 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4199 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4200 ExpectSyncedAndDeleted();
4201
4202 // Undelete it locally.
4203 Undelete();
4204 ExpectUnsyncedUndeletion();
4205 SyncShareAsDelegate();
4206 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4207 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4208 ExpectSyncedAndCreated();
4209
4210 // Now, encounter a GetUpdates corresponding to the just-committed
4211 // deletion update. The undeletion should prevail.
4212 mock_server_->AddUpdateFromLastCommit();
4213 SyncShareAsDelegate();
4214 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4215 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4216 ExpectSyncedAndCreated();
4217 }
4218
4219 TEST_F(SyncerUndeletionTest, OtherClientUndeletes) {
4220 Create();
4221 ExpectUnsyncedCreation();
4222 SyncShareAsDelegate();
4223
4224 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4225 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4226 ExpectSyncedAndCreated();
4227
4228 // Get the updates of our just-committed entry.
4229 mock_server_->AddUpdateFromLastCommit();
4230 SyncShareAsDelegate();
4231 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4232 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4233 ExpectSyncedAndCreated();
4234
4235 // We delete the item.
4236 Delete();
4237 ExpectUnsyncedDeletion();
4238 SyncShareAsDelegate();
4239
4240 // The update ought to have applied successfully.
4241 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4242 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4243 ExpectSyncedAndDeleted();
4244
4245 // Now, encounter a GetUpdates corresponding to the just-committed
4246 // deletion update.
4247 mock_server_->AddUpdateFromLastCommit();
4248 SyncShareAsDelegate();
4249 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4250 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4251 ExpectSyncedAndDeleted();
4252
4253 // Some other client undeletes the item.
4254 mock_server_->AddUpdateBookmark(Get(metahandle_, ID),
4255 Get(metahandle_, PARENT_ID),
4256 "Thadeusz", 100, 1000);
4257 mock_server_->SetLastUpdateClientTag(client_tag_);
4258 SyncShareAsDelegate();
4259 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4260 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4261 ExpectSyncedAndCreated();
4262 EXPECT_EQ("Thadeusz", Get(metahandle_, NON_UNIQUE_NAME));
4263 }
4264
4265 TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) {
4266 Create();
4267 ExpectUnsyncedCreation();
4268 SyncShareAsDelegate();
4269
4270 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4271 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4272 ExpectSyncedAndCreated();
4273
4274 // Get the updates of our just-committed entry.
4275 mock_server_->AddUpdateFromLastCommit();
4276 SyncShareAsDelegate();
4277 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4278 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4279 ExpectSyncedAndCreated();
4280
4281 // We delete the item.
4282 Delete();
4283 ExpectUnsyncedDeletion();
4284 SyncShareAsDelegate();
4285
4286 // The update ought to have applied successfully.
4287 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4288 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4289 ExpectSyncedAndDeleted();
4290
4291 // Some other client undeletes before we see the update from our
4292 // commit.
4293 mock_server_->AddUpdateBookmark(Get(metahandle_, ID),
4294 Get(metahandle_, PARENT_ID),
4295 "Thadeusz", 100, 1000);
4296 mock_server_->SetLastUpdateClientTag(client_tag_);
4297 SyncShareAsDelegate();
4298 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems());
4299 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests());
4300 ExpectSyncedAndCreated();
4301 EXPECT_EQ("Thadeusz", Get(metahandle_, NON_UNIQUE_NAME));
4302 }
4303
4304 // A group of tests exercising the syncer's handling of sibling ordering, as
4305 // represented in the sync protocol.
4306 class SyncerPositionUpdateTest : public SyncerTest {
4307 public:
4308 SyncerPositionUpdateTest() : next_update_id_(1), next_revision_(1) {}
4309
4310 protected:
4311 void ExpectLocalItemsInServerOrder() {
4312 if (position_map_.empty())
4313 return;
4314
4315 ReadTransaction trans(FROM_HERE, directory());
4316
4317 Id prev_id;
4318 DCHECK(prev_id.IsRoot());
4319 PosMap::iterator next = position_map_.begin();
4320 for (PosMap::iterator i = next++; i != position_map_.end(); ++i) {
4321 Id id = i->second;
4322 Entry entry_with_id(&trans, GET_BY_ID, id);
4323 EXPECT_TRUE(entry_with_id.good());
4324 EXPECT_EQ(prev_id, entry_with_id.Get(PREV_ID));
4325 EXPECT_EQ(i->first, entry_with_id.Get(SERVER_POSITION_IN_PARENT));
4326 if (next == position_map_.end()) {
4327 EXPECT_EQ(Id(), entry_with_id.Get(NEXT_ID));
4328 } else {
4329 EXPECT_EQ(next->second, entry_with_id.Get(NEXT_ID));
4330 next++;
4331 }
4332 prev_id = id;
4333 }
4334 }
4335
4336 void AddRootItemWithPosition(int64 position) {
4337 string id = string("ServerId") + base::Int64ToString(next_update_id_++);
4338 string name = "my name is my id -- " + id;
4339 int revision = next_revision_++;
4340 mock_server_->AddUpdateDirectory(id, kRootId, name, revision, revision);
4341 mock_server_->SetLastUpdatePosition(position);
4342 position_map_.insert(
4343 PosMap::value_type(position, Id::CreateFromServerId(id)));
4344 }
4345 private:
4346 typedef multimap<int64, Id> PosMap;
4347 PosMap position_map_;
4348 int next_update_id_;
4349 int next_revision_;
4350 DISALLOW_COPY_AND_ASSIGN(SyncerPositionUpdateTest);
4351 };
4352
4353 TEST_F(SyncerPositionUpdateTest, InOrderPositive) {
4354 // Add a bunch of items in increasing order, starting with just positive
4355 // position values.
4356 AddRootItemWithPosition(100);
4357 AddRootItemWithPosition(199);
4358 AddRootItemWithPosition(200);
4359 AddRootItemWithPosition(201);
4360 AddRootItemWithPosition(400);
4361
4362 SyncShareAsDelegate();
4363 ExpectLocalItemsInServerOrder();
4364 }
4365
4366 TEST_F(SyncerPositionUpdateTest, InOrderNegative) {
4367 // Test negative position values, but in increasing order.
4368 AddRootItemWithPosition(-400);
4369 AddRootItemWithPosition(-201);
4370 AddRootItemWithPosition(-200);
4371 AddRootItemWithPosition(-150);
4372 AddRootItemWithPosition(100);
4373
4374 SyncShareAsDelegate();
4375 ExpectLocalItemsInServerOrder();
4376 }
4377
4378 TEST_F(SyncerPositionUpdateTest, ReverseOrder) {
4379 // Test when items are sent in the reverse order.
4380 AddRootItemWithPosition(400);
4381 AddRootItemWithPosition(201);
4382 AddRootItemWithPosition(200);
4383 AddRootItemWithPosition(100);
4384 AddRootItemWithPosition(-150);
4385 AddRootItemWithPosition(-201);
4386 AddRootItemWithPosition(-200);
4387 AddRootItemWithPosition(-400);
4388
4389 SyncShareAsDelegate();
4390 ExpectLocalItemsInServerOrder();
4391 }
4392
4393 TEST_F(SyncerPositionUpdateTest, RandomOrderInBatches) {
4394 // Mix it all up, interleaving position values, and try multiple batches of
4395 // updates.
4396 AddRootItemWithPosition(400);
4397 AddRootItemWithPosition(201);
4398 AddRootItemWithPosition(-400);
4399 AddRootItemWithPosition(100);
4400
4401 SyncShareAsDelegate();
4402 ExpectLocalItemsInServerOrder();
4403
4404 AddRootItemWithPosition(-150);
4405 AddRootItemWithPosition(-200);
4406 AddRootItemWithPosition(200);
4407 AddRootItemWithPosition(-201);
4408
4409 SyncShareAsDelegate();
4410 ExpectLocalItemsInServerOrder();
4411
4412 AddRootItemWithPosition(-144);
4413
4414 SyncShareAsDelegate();
4415 ExpectLocalItemsInServerOrder();
4416 }
4417
4418 class SyncerPositionTiebreakingTest : public SyncerTest {
4419 public:
4420 SyncerPositionTiebreakingTest()
4421 : low_id_(Id::CreateFromServerId("A")),
4422 mid_id_(Id::CreateFromServerId("M")),
4423 high_id_(Id::CreateFromServerId("Z")),
4424 next_revision_(1) {
4425 DCHECK(low_id_ < mid_id_);
4426 DCHECK(mid_id_ < high_id_);
4427 DCHECK(low_id_ < high_id_);
4428 }
4429
4430 // Adds the item by its Id, using a constant value for the position
4431 // so that the syncer has to resolve the order some other way.
4432 void Add(const Id& id) {
4433 int revision = next_revision_++;
4434 mock_server_->AddUpdateDirectory(id.GetServerId(), kRootId,
4435 id.GetServerId(), revision, revision);
4436 // The update position doesn't vary.
4437 mock_server_->SetLastUpdatePosition(90210);
4438 }
4439
4440 void ExpectLocalOrderIsByServerId() {
4441 ReadTransaction trans(FROM_HERE, directory());
4442 Id null_id;
4443 Entry low(&trans, GET_BY_ID, low_id_);
4444 Entry mid(&trans, GET_BY_ID, mid_id_);
4445 Entry high(&trans, GET_BY_ID, high_id_);
4446 EXPECT_TRUE(low.good());
4447 EXPECT_TRUE(mid.good());
4448 EXPECT_TRUE(high.good());
4449 EXPECT_TRUE(low.Get(PREV_ID) == null_id);
4450 EXPECT_TRUE(mid.Get(PREV_ID) == low_id_);
4451 EXPECT_TRUE(high.Get(PREV_ID) == mid_id_);
4452 EXPECT_TRUE(high.Get(NEXT_ID) == null_id);
4453 EXPECT_TRUE(mid.Get(NEXT_ID) == high_id_);
4454 EXPECT_TRUE(low.Get(NEXT_ID) == mid_id_);
4455 }
4456
4457 protected:
4458 // When there's a tiebreak on the numeric position, it's supposed to be
4459 // broken by string comparison of the ids. These ids are in increasing
4460 // order.
4461 const Id low_id_;
4462 const Id mid_id_;
4463 const Id high_id_;
4464
4465 private:
4466 int next_revision_;
4467 DISALLOW_COPY_AND_ASSIGN(SyncerPositionTiebreakingTest);
4468 };
4469
4470 TEST_F(SyncerPositionTiebreakingTest, LowMidHigh) {
4471 Add(low_id_);
4472 Add(mid_id_);
4473 Add(high_id_);
4474 SyncShareAsDelegate();
4475 ExpectLocalOrderIsByServerId();
4476 }
4477
4478 TEST_F(SyncerPositionTiebreakingTest, LowHighMid) {
4479 Add(low_id_);
4480 Add(high_id_);
4481 Add(mid_id_);
4482 SyncShareAsDelegate();
4483 ExpectLocalOrderIsByServerId();
4484 }
4485
4486 TEST_F(SyncerPositionTiebreakingTest, HighMidLow) {
4487 Add(high_id_);
4488 Add(mid_id_);
4489 Add(low_id_);
4490 SyncShareAsDelegate();
4491 ExpectLocalOrderIsByServerId();
4492 }
4493
4494 TEST_F(SyncerPositionTiebreakingTest, HighLowMid) {
4495 Add(high_id_);
4496 Add(low_id_);
4497 Add(mid_id_);
4498 SyncShareAsDelegate();
4499 ExpectLocalOrderIsByServerId();
4500 }
4501
4502 TEST_F(SyncerPositionTiebreakingTest, MidHighLow) {
4503 Add(mid_id_);
4504 Add(high_id_);
4505 Add(low_id_);
4506 SyncShareAsDelegate();
4507 ExpectLocalOrderIsByServerId();
4508 }
4509
4510 TEST_F(SyncerPositionTiebreakingTest, MidLowHigh) {
4511 Add(mid_id_);
4512 Add(low_id_);
4513 Add(high_id_);
4514 SyncShareAsDelegate();
4515 ExpectLocalOrderIsByServerId();
4516 }
4517
4518 const SyncerTest::CommitOrderingTest
4519 SyncerTest::CommitOrderingTest::LAST_COMMIT_ITEM = {-1, TestIdFactory::root()};
4520
4521 } // namespace browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/sync/engine/syncer_types.cc ('k') | chrome/browser/sync/engine/syncer_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698