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