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