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

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

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

Powered by Google App Engine
This is Rietveld 408576698