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