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