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