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