syncer_unittest.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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 3252TEST_F(SyncerTest, DirectoryUpdateTest) { 3253 Id in_root_id = ids_.NewServerId(); 3254 Id in_in_root_id = ids_.NewServerId(); 3255 3256 mock_server_->AddUpdateDirectory(in_root_id, TestIdFactory::root(), 3257 "in_root_name", 2, 2, 3258 foreign_cache_guid(), "-1"); 3259 mock_server_->AddUpdateDirectory(in_in_root_id, in_root_id, 3260 "in_in_root_name", 3, 3, 3261 foreign_cache_guid(), "-2"); 3262 SyncShareNudge(); 3263 { 3264 syncable::ReadTransaction trans(FROM_HERE, directory()); 3265 Entry in_root(&trans, GET_BY_ID, in_root_id); 3266 ASSERT_TRUE(in_root.good()); 3267 EXPECT_EQ("in_root_name", in_root.GetNonUniqueName()); 3268 EXPECT_EQ(TestIdFactory::root(), in_root.GetParentId()); 3269 3270 Entry in_in_root(&trans, GET_BY_ID, in_in_root_id); 3271 ASSERT_TRUE(in_in_root.good()); 3272 EXPECT_EQ("in_in_root_name", in_in_root.GetNonUniqueName()); 3273 EXPECT_EQ(in_root_id, in_in_root.GetParentId()); 3274 } 3275} 3276 3277TEST_F(SyncerTest, DirectoryCommitTest) { 3278 syncable::Id in_root_id, in_dir_id; 3279 int64 foo_metahandle; 3280 int64 bar_metahandle; 3281 3282 { 3283 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); 3284 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "foo"); 3285 ASSERT_TRUE(parent.good()); 3286 parent.PutIsUnsynced(true); 3287 parent.PutIsDir(true); 3288 parent.PutSpecifics(DefaultBookmarkSpecifics()); 3289 in_root_id = parent.GetId(); 3290 foo_metahandle = parent.GetMetahandle(); 3291 3292 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "bar"); 3293 ASSERT_TRUE(child.good()); 3294 child.PutIsUnsynced(true); 3295 child.PutIsDir(true); 3296 child.PutSpecifics(DefaultBookmarkSpecifics()); 3297 bar_metahandle = child.GetMetahandle(); 3298 in_dir_id = parent.GetId(); 3299 } 3300 SyncShareNudge(); 3301 { 3302 syncable::ReadTransaction trans(FROM_HERE, directory()); 3303 Entry fail_by_old_id_entry(&trans, GET_BY_ID, in_root_id); 3304 ASSERT_FALSE(fail_by_old_id_entry.good()); 3305 3306 Entry foo_entry(&trans, GET_BY_HANDLE, foo_metahandle); 3307 ASSERT_TRUE(foo_entry.good()); 3308 EXPECT_EQ("foo", foo_entry.GetNonUniqueName()); 3309 EXPECT_NE(foo_entry.GetId(), in_root_id); 3310 3311 Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle); 3312 ASSERT_TRUE(bar_entry.good()); 3313 EXPECT_EQ("bar", bar_entry.GetNonUniqueName()); 3314 EXPECT_NE(bar_entry.GetId(), in_dir_id); 3315 EXPECT_EQ(foo_entry.GetId(), bar_entry.GetParentId()); 3316 } 3317} 3318 3319TEST_F(SyncerTest, TestClientCommandDuringUpdate) { 3320 using sync_pb::ClientCommand; 3321 3322 ClientCommand* command = new ClientCommand(); 3323 command->set_set_sync_poll_interval(8); 3324 command->set_set_sync_long_poll_interval(800); 3325 command->set_sessions_commit_delay_seconds(3141); 3326 command->set_client_invalidation_hint_buffer_size(11); 3327 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1, 3328 foreign_cache_guid(), "-1"); 3329 mock_server_->SetGUClientCommand(command); 3330 SyncShareNudge(); 3331 3332 EXPECT_TRUE(TimeDelta::FromSeconds(8) == 3333 last_short_poll_interval_received_); 3334 EXPECT_TRUE(TimeDelta::FromSeconds(800) == 3335 last_long_poll_interval_received_); 3336 EXPECT_TRUE(TimeDelta::FromSeconds(3141) == 3337 last_sessions_commit_delay_seconds_); 3338 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_); 3339 3340 command = new ClientCommand(); 3341 command->set_set_sync_poll_interval(180); 3342 command->set_set_sync_long_poll_interval(190); 3343 command->set_sessions_commit_delay_seconds(2718); 3344 command->set_client_invalidation_hint_buffer_size(9); 3345 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1, 3346 foreign_cache_guid(), "-1"); 3347 mock_server_->SetGUClientCommand(command); 3348 SyncShareNudge(); 3349 3350 EXPECT_TRUE(TimeDelta::FromSeconds(180) == 3351 last_short_poll_interval_received_); 3352 EXPECT_TRUE(TimeDelta::FromSeconds(190) == 3353 last_long_poll_interval_received_); 3354 EXPECT_TRUE(TimeDelta::FromSeconds(2718) == 3355 last_sessions_commit_delay_seconds_); 3356 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_); 3357} 3358 3359TEST_F(SyncerTest, TestClientCommandDuringCommit) { 3360 using sync_pb::ClientCommand; 3361 3362 ClientCommand* command = new ClientCommand(); 3363 command->set_set_sync_poll_interval(8); 3364 command->set_set_sync_long_poll_interval(800); 3365 command->set_sessions_commit_delay_seconds(3141); 3366 command->set_client_invalidation_hint_buffer_size(11); 3367 CreateUnsyncedDirectory("X", "id_X"); 3368 mock_server_->SetCommitClientCommand(command); 3369 SyncShareNudge(); 3370 3371 EXPECT_TRUE(TimeDelta::FromSeconds(8) == 3372 last_short_poll_interval_received_); 3373 EXPECT_TRUE(TimeDelta::FromSeconds(800) == 3374 last_long_poll_interval_received_); 3375 EXPECT_TRUE(TimeDelta::FromSeconds(3141) == 3376 last_sessions_commit_delay_seconds_); 3377 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_); 3378 3379 command = new ClientCommand(); 3380 command->set_set_sync_poll_interval(180); 3381 command->set_set_sync_long_poll_interval(190); 3382 command->set_sessions_commit_delay_seconds(2718); 3383 command->set_client_invalidation_hint_buffer_size(9); 3384 CreateUnsyncedDirectory("Y", "id_Y"); 3385 mock_server_->SetCommitClientCommand(command); 3386 SyncShareNudge(); 3387 3388 EXPECT_TRUE(TimeDelta::FromSeconds(180) == 3389 last_short_poll_interval_received_); 3390 EXPECT_TRUE(TimeDelta::FromSeconds(190) == 3391 last_long_poll_interval_received_); 3392 EXPECT_TRUE(TimeDelta::FromSeconds(2718) == 3393 last_sessions_commit_delay_seconds_); 3394 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_); 3395} 3396 3397TEST_F(SyncerTest, EnsureWeSendUpOldParent) { 3398 syncable::Id folder_one_id = ids_.FromNumber(1); 3399 syncable::Id folder_two_id = ids_.FromNumber(2); 3400 3401 mock_server_->AddUpdateDirectory(folder_one_id, TestIdFactory::root(), 3402 "folder_one", 1, 1, foreign_cache_guid(), "-1"); 3403 mock_server_->AddUpdateDirectory(folder_two_id, TestIdFactory::root(), 3404 "folder_two", 1, 1, foreign_cache_guid(), "-2"); 3405 SyncShareNudge(); 3406 { 3407 // A moved entry should send an "old parent." 3408 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 3409 MutableEntry entry(&trans, GET_BY_ID, folder_one_id); 3410 ASSERT_TRUE(entry.good()); 3411 entry.PutParentId(folder_two_id); 3412 entry.PutIsUnsynced(true); 3413 // A new entry should send no "old parent." 3414 MutableEntry create( 3415 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder"); 3416 create.PutIsUnsynced(true); 3417 create.PutSpecifics(DefaultBookmarkSpecifics()); 3418 } 3419 SyncShareNudge(); 3420 const sync_pb::CommitMessage& commit = mock_server_->last_sent_commit(); 3421 ASSERT_EQ(2, commit.entries_size()); 3422 EXPECT_TRUE(commit.entries(0).parent_id_string() == "2"); 3423 EXPECT_TRUE(commit.entries(0).old_parent_id() == "0"); 3424 EXPECT_FALSE(commit.entries(1).has_old_parent_id()); 3425} 3426 3427TEST_F(SyncerTest, Test64BitVersionSupport) { 3428 int64 really_big_int = std::numeric_limits<int64>::max() - 12; 3429 const string name("ringo's dang orang ran rings around my o-ring"); 3430 int64 item_metahandle; 3431 3432 // Try writing max int64 to the version fields of a meta entry. 3433 { 3434 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); 3435 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name); 3436 ASSERT_TRUE(entry.good()); 3437 entry.PutBaseVersion(really_big_int); 3438 entry.PutServerVersion(really_big_int); 3439 entry.PutId(ids_.NewServerId()); 3440 item_metahandle = entry.GetMetahandle(); 3441 } 3442 // Now read it back out and make sure the value is max int64. 3443 syncable::ReadTransaction rtrans(FROM_HERE, directory()); 3444 Entry entry(&rtrans, syncable::GET_BY_HANDLE, item_metahandle); 3445 ASSERT_TRUE(entry.good()); 3446 EXPECT_TRUE(really_big_int == entry.GetBaseVersion()); 3447} 3448 3449TEST_F(SyncerTest, TestSimpleUndelete) { 3450 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root(); 3451 mock_server_->set_conflict_all_commits(true); 3452 // Let there be an entry from the server. 3453 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10, 3454 foreign_cache_guid(), "-1"); 3455 SyncShareNudge(); 3456 // Check it out and delete it. 3457 { 3458 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); 3459 MutableEntry entry(&wtrans, GET_BY_ID, id); 3460 ASSERT_TRUE(entry.good()); 3461 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 3462 EXPECT_FALSE(entry.GetIsUnsynced()); 3463 EXPECT_FALSE(entry.GetIsDel()); 3464 // Delete it locally. 3465 entry.PutIsDel(true); 3466 } 3467 SyncShareNudge(); 3468 // Confirm we see IS_DEL and not SERVER_IS_DEL. 3469 { 3470 syncable::ReadTransaction trans(FROM_HERE, directory()); 3471 Entry entry(&trans, GET_BY_ID, id); 3472 ASSERT_TRUE(entry.good()); 3473 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 3474 EXPECT_FALSE(entry.GetIsUnsynced()); 3475 EXPECT_TRUE(entry.GetIsDel()); 3476 EXPECT_FALSE(entry.GetServerIsDel()); 3477 } 3478 SyncShareNudge(); 3479 // Update from server confirming deletion. 3480 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 11, 3481 foreign_cache_guid(), "-1"); 3482 mock_server_->SetLastUpdateDeleted(); 3483 SyncShareNudge(); 3484 // IS_DEL AND SERVER_IS_DEL now both true. 3485 { 3486 syncable::ReadTransaction trans(FROM_HERE, directory()); 3487 Entry entry(&trans, GET_BY_ID, id); 3488 ASSERT_TRUE(entry.good()); 3489 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 3490 EXPECT_FALSE(entry.GetIsUnsynced()); 3491 EXPECT_TRUE(entry.GetIsDel()); 3492 EXPECT_TRUE(entry.GetServerIsDel()); 3493 } 3494 // Undelete from server. 3495 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12, 3496 foreign_cache_guid(), "-1"); 3497 SyncShareNudge(); 3498 // IS_DEL and SERVER_IS_DEL now both false. 3499 { 3500 syncable::ReadTransaction trans(FROM_HERE, directory()); 3501 Entry entry(&trans, GET_BY_ID, id); 3502 ASSERT_TRUE(entry.good()); 3503 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 3504 EXPECT_FALSE(entry.GetIsUnsynced()); 3505 EXPECT_FALSE(entry.GetIsDel()); 3506 EXPECT_FALSE(entry.GetServerIsDel()); 3507 } 3508} 3509 3510TEST_F(SyncerTest, TestUndeleteWithMissingDeleteUpdate) { 3511 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root(); 3512 // Let there be a entry, from the server. 3513 mock_server_->set_conflict_all_commits(true); 3514 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10, 3515 foreign_cache_guid(), "-1"); 3516 SyncShareNudge(); 3517 // Check it out and delete it. 3518 { 3519 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); 3520 MutableEntry entry(&wtrans, GET_BY_ID, id); 3521 ASSERT_TRUE(entry.good()); 3522 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 3523 EXPECT_FALSE(entry.GetIsUnsynced()); 3524 EXPECT_FALSE(entry.GetIsDel()); 3525 // Delete it locally. 3526 entry.PutIsDel(true); 3527 } 3528 SyncShareNudge(); 3529 // Confirm we see IS_DEL and not SERVER_IS_DEL. 3530 { 3531 syncable::ReadTransaction trans(FROM_HERE, directory()); 3532 Entry entry(&trans, GET_BY_ID, id); 3533 ASSERT_TRUE(entry.good()); 3534 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 3535 EXPECT_FALSE(entry.GetIsUnsynced()); 3536 EXPECT_TRUE(entry.GetIsDel()); 3537 EXPECT_FALSE(entry.GetServerIsDel()); 3538 } 3539 SyncShareNudge(); 3540 // Say we do not get an update from server confirming deletion. Undelete 3541 // from server 3542 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12, 3543 foreign_cache_guid(), "-1"); 3544 SyncShareNudge(); 3545 // IS_DEL and SERVER_IS_DEL now both false. 3546 { 3547 syncable::ReadTransaction trans(FROM_HERE, directory()); 3548 Entry entry(&trans, GET_BY_ID, id); 3549 ASSERT_TRUE(entry.good()); 3550 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 3551 EXPECT_FALSE(entry.GetIsUnsynced()); 3552 EXPECT_FALSE(entry.GetIsDel()); 3553 EXPECT_FALSE(entry.GetServerIsDel()); 3554 } 3555} 3556 3557TEST_F(SyncerTest, TestUndeleteIgnoreCorrectlyUnappliedUpdate) { 3558 Id id1 = ids_.MakeServer("first"), id2 = ids_.MakeServer("second"); 3559 Id root = TestIdFactory::root(); 3560 // Duplicate! expect path clashing! 3561 mock_server_->set_conflict_all_commits(true); 3562 mock_server_->AddUpdateBookmark(id1, root, "foo", 1, 10, 3563 foreign_cache_guid(), "-1"); 3564 mock_server_->AddUpdateBookmark(id2, root, "foo", 1, 10, 3565 foreign_cache_guid(), "-2"); 3566 SyncShareNudge(); 3567 mock_server_->AddUpdateBookmark(id2, root, "foo2", 2, 20, 3568 foreign_cache_guid(), "-2"); 3569 SyncShareNudge(); // Now just don't explode. 3570} 3571 3572TEST_F(SyncerTest, ClientTagServerCreatedUpdatesWork) { 3573 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10, 3574 foreign_cache_guid(), "-1"); 3575 mock_server_->SetLastUpdateClientTag("permfolder"); 3576 3577 SyncShareNudge(); 3578 3579 { 3580 syncable::ReadTransaction trans(FROM_HERE, directory()); 3581 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder"); 3582 ASSERT_TRUE(perm_folder.good()); 3583 EXPECT_FALSE(perm_folder.GetIsDel()); 3584 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); 3585 EXPECT_FALSE(perm_folder.GetIsUnsynced()); 3586 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder"); 3587 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1"); 3588 } 3589 3590 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100, 3591 foreign_cache_guid(), "-1"); 3592 mock_server_->SetLastUpdateClientTag("permfolder"); 3593 SyncShareNudge(); 3594 3595 { 3596 syncable::ReadTransaction trans(FROM_HERE, directory()); 3597 3598 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder"); 3599 ASSERT_TRUE(perm_folder.good()); 3600 EXPECT_FALSE(perm_folder.GetIsDel()); 3601 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); 3602 EXPECT_FALSE(perm_folder.GetIsUnsynced()); 3603 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder"); 3604 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem_renamed"); 3605 } 3606} 3607 3608TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) { 3609 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10, 3610 foreign_cache_guid(), "-1"); 3611 mock_server_->SetLastUpdateClientTag("permfolder"); 3612 3613 SyncShareNudge(); 3614 3615 { 3616 syncable::ReadTransaction trans(FROM_HERE, directory()); 3617 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder"); 3618 ASSERT_TRUE(perm_folder.good()); 3619 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); 3620 EXPECT_FALSE(perm_folder.GetIsUnsynced()); 3621 EXPECT_EQ(perm_folder.GetUniqueClientTag(), "permfolder"); 3622 EXPECT_TRUE(perm_folder.GetNonUniqueName()== "permitem1"); 3623 EXPECT_TRUE(perm_folder.GetId().ServerKnows()); 3624 } 3625 3626 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100, 3627 foreign_cache_guid(), "-1"); 3628 mock_server_->SetLastUpdateClientTag("wrongtag"); 3629 SyncShareNudge(); 3630 3631 { 3632 syncable::ReadTransaction trans(FROM_HERE, directory()); 3633 3634 // This update is rejected because it has the same ID, but a 3635 // different tag than one that is already on the client. 3636 // The client has a ServerKnows ID, which cannot be overwritten. 3637 Entry rejected_update(&trans, GET_BY_CLIENT_TAG, "wrongtag"); 3638 EXPECT_FALSE(rejected_update.good()); 3639 3640 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder"); 3641 ASSERT_TRUE(perm_folder.good()); 3642 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); 3643 EXPECT_FALSE(perm_folder.GetIsUnsynced()); 3644 EXPECT_EQ(perm_folder.GetNonUniqueName(), "permitem1"); 3645 } 3646} 3647 3648TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) { 3649 int64 original_metahandle = 0; 3650 3651 { 3652 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 3653 MutableEntry pref( 3654 &trans, CREATE, PREFERENCES, ids_.root(), "name"); 3655 ASSERT_TRUE(pref.good()); 3656 pref.PutUniqueClientTag("tag"); 3657 pref.PutIsUnsynced(true); 3658 EXPECT_FALSE(pref.GetIsUnappliedUpdate()); 3659 EXPECT_FALSE(pref.GetId().ServerKnows()); 3660 original_metahandle = pref.GetMetahandle(); 3661 } 3662 3663 syncable::Id server_id = TestIdFactory::MakeServer("id"); 3664 mock_server_->AddUpdatePref(server_id.GetServerId(), 3665 ids_.root().GetServerId(), 3666 "tag", 10, 100); 3667 mock_server_->set_conflict_all_commits(true); 3668 3669 SyncShareNudge(); 3670 // This should cause client tag reunion, preserving the metahandle. 3671 { 3672 syncable::ReadTransaction trans(FROM_HERE, directory()); 3673 3674 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag"); 3675 ASSERT_TRUE(pref.good()); 3676 EXPECT_FALSE(pref.GetIsDel()); 3677 EXPECT_FALSE(pref.GetIsUnappliedUpdate()); 3678 EXPECT_TRUE(pref.GetIsUnsynced()); 3679 EXPECT_EQ(10, pref.GetBaseVersion()); 3680 // Entry should have been given the new ID while preserving the 3681 // metahandle; client should have won the conflict resolution. 3682 EXPECT_EQ(original_metahandle, pref.GetMetahandle()); 3683 EXPECT_EQ("tag", pref.GetUniqueClientTag()); 3684 EXPECT_TRUE(pref.GetId().ServerKnows()); 3685 } 3686 3687 mock_server_->set_conflict_all_commits(false); 3688 SyncShareNudge(); 3689 3690 // The resolved entry ought to commit cleanly. 3691 { 3692 syncable::ReadTransaction trans(FROM_HERE, directory()); 3693 3694 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag"); 3695 ASSERT_TRUE(pref.good()); 3696 EXPECT_FALSE(pref.GetIsDel()); 3697 EXPECT_FALSE(pref.GetIsUnappliedUpdate()); 3698 EXPECT_FALSE(pref.GetIsUnsynced()); 3699 EXPECT_TRUE(10 < pref.GetBaseVersion()); 3700 // Entry should have been given the new ID while preserving the 3701 // metahandle; client should have won the conflict resolution. 3702 EXPECT_EQ(original_metahandle, pref.GetMetahandle()); 3703 EXPECT_EQ("tag", pref.GetUniqueClientTag()); 3704 EXPECT_TRUE(pref.GetId().ServerKnows()); 3705 } 3706} 3707 3708TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) { 3709 { 3710 // Create a deleted local entry with a unique client tag. 3711 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 3712 MutableEntry pref( 3713 &trans, CREATE, PREFERENCES, ids_.root(), "name"); 3714 ASSERT_TRUE(pref.good()); 3715 ASSERT_FALSE(pref.GetId().ServerKnows()); 3716 pref.PutUniqueClientTag("tag"); 3717 pref.PutIsUnsynced(true); 3718 3719 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit. 3720 // (We never attempt to commit server-unknown deleted items, so this 3721 // helps us clean up those entries). 3722 pref.PutIsDel(true); 3723 } 3724 3725 // Prepare an update with the same unique client tag. 3726 syncable::Id server_id = TestIdFactory::MakeServer("id"); 3727 mock_server_->AddUpdatePref(server_id.GetServerId(), 3728 ids_.root().GetServerId(), 3729 "tag", 10, 100); 3730 3731 SyncShareNudge(); 3732 // The local entry will be overwritten. 3733 { 3734 syncable::ReadTransaction trans(FROM_HERE, directory()); 3735 3736 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag"); 3737 ASSERT_TRUE(pref.good()); 3738 ASSERT_TRUE(pref.GetId().ServerKnows()); 3739 EXPECT_FALSE(pref.GetIsDel()); 3740 EXPECT_FALSE(pref.GetIsUnappliedUpdate()); 3741 EXPECT_FALSE(pref.GetIsUnsynced()); 3742 EXPECT_EQ(pref.GetBaseVersion(), 10); 3743 EXPECT_EQ(pref.GetUniqueClientTag(), "tag"); 3744 } 3745} 3746 3747TEST_F(SyncerTest, ClientTagUpdateClashesWithLocalEntry) { 3748 // This test is written assuming that ID comparison 3749 // will work out in a particular way. 3750 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(2)); 3751 EXPECT_TRUE(ids_.FromNumber(3) < ids_.FromNumber(4)); 3752 3753 syncable::Id id1 = TestIdFactory::MakeServer("1"); 3754 mock_server_->AddUpdatePref(id1.GetServerId(), ids_.root().GetServerId(), 3755 "tag1", 10, 100); 3756 3757 syncable::Id id4 = TestIdFactory::MakeServer("4"); 3758 mock_server_->AddUpdatePref(id4.GetServerId(), ids_.root().GetServerId(), 3759 "tag2", 11, 110); 3760 3761 mock_server_->set_conflict_all_commits(true); 3762 3763 SyncShareNudge(); 3764 int64 tag1_metahandle = syncable::kInvalidMetaHandle; 3765 int64 tag2_metahandle = syncable::kInvalidMetaHandle; 3766 // This should cause client tag overwrite. 3767 { 3768 syncable::ReadTransaction trans(FROM_HERE, directory()); 3769 3770 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1"); 3771 ASSERT_TRUE(tag1.good()); 3772 ASSERT_TRUE(tag1.GetId().ServerKnows()); 3773 ASSERT_TRUE(id1 == tag1.GetId()); 3774 EXPECT_FALSE(tag1.GetIsDel()); 3775 EXPECT_FALSE(tag1.GetIsUnappliedUpdate()); 3776 EXPECT_FALSE(tag1.GetIsUnsynced()); 3777 EXPECT_EQ(10, tag1.GetBaseVersion()); 3778 EXPECT_EQ("tag1", tag1.GetUniqueClientTag()); 3779 tag1_metahandle = tag1.GetMetahandle(); 3780 3781 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2"); 3782 ASSERT_TRUE(tag2.good()); 3783 ASSERT_TRUE(tag2.GetId().ServerKnows()); 3784 ASSERT_TRUE(id4 == tag2.GetId()); 3785 EXPECT_FALSE(tag2.GetIsDel()); 3786 EXPECT_FALSE(tag2.GetIsUnappliedUpdate()); 3787 EXPECT_FALSE(tag2.GetIsUnsynced()); 3788 EXPECT_EQ(11, tag2.GetBaseVersion()); 3789 EXPECT_EQ("tag2", tag2.GetUniqueClientTag()); 3790 tag2_metahandle = tag2.GetMetahandle(); 3791 3792 syncable::Directory::Metahandles children; 3793 directory()->GetChildHandlesById(&trans, trans.root_id(), &children); 3794 ASSERT_EQ(2U, children.size()); 3795 } 3796 3797 syncable::Id id2 = TestIdFactory::MakeServer("2"); 3798 mock_server_->AddUpdatePref(id2.GetServerId(), ids_.root().GetServerId(), 3799 "tag1", 12, 120); 3800 syncable::Id id3 = TestIdFactory::MakeServer("3"); 3801 mock_server_->AddUpdatePref(id3.GetServerId(), ids_.root().GetServerId(), 3802 "tag2", 13, 130); 3803 SyncShareNudge(); 3804 3805 { 3806 syncable::ReadTransaction trans(FROM_HERE, directory()); 3807 3808 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1"); 3809 ASSERT_TRUE(tag1.good()); 3810 ASSERT_TRUE(tag1.GetId().ServerKnows()); 3811 ASSERT_EQ(id1, tag1.GetId()) 3812 << "ID 1 should be kept, since it was less than ID 2."; 3813 EXPECT_FALSE(tag1.GetIsDel()); 3814 EXPECT_FALSE(tag1.GetIsUnappliedUpdate()); 3815 EXPECT_FALSE(tag1.GetIsUnsynced()); 3816 EXPECT_EQ(10, tag1.GetBaseVersion()); 3817 EXPECT_EQ("tag1", tag1.GetUniqueClientTag()); 3818 EXPECT_EQ(tag1_metahandle, tag1.GetMetahandle()); 3819 3820 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2"); 3821 ASSERT_TRUE(tag2.good()); 3822 ASSERT_TRUE(tag2.GetId().ServerKnows()); 3823 ASSERT_EQ(id3, tag2.GetId()) 3824 << "ID 3 should be kept, since it was less than ID 4."; 3825 EXPECT_FALSE(tag2.GetIsDel()); 3826 EXPECT_FALSE(tag2.GetIsUnappliedUpdate()); 3827 EXPECT_FALSE(tag2.GetIsUnsynced()); 3828 EXPECT_EQ(13, tag2.GetBaseVersion()); 3829 EXPECT_EQ("tag2", tag2.GetUniqueClientTag()); 3830 EXPECT_EQ(tag2_metahandle, tag2.GetMetahandle()); 3831 3832 syncable::Directory::Metahandles children; 3833 directory()->GetChildHandlesById(&trans, trans.root_id(), &children); 3834 ASSERT_EQ(2U, children.size()); 3835 } 3836} 3837 3838TEST_F(SyncerTest, ClientTagClashWithinBatchOfUpdates) { 3839 // This test is written assuming that ID comparison 3840 // will work out in a particular way. 3841 EXPECT_TRUE(ids_.FromNumber(1) < ids_.FromNumber(4)); 3842 EXPECT_TRUE(ids_.FromNumber(201) < ids_.FromNumber(205)); 3843 3844 // Least ID: winner. 3845 mock_server_->AddUpdatePref(ids_.FromNumber(1).GetServerId(), 3846 ids_.root().GetServerId(), "tag a", 1, 10); 3847 mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(), 3848 ids_.root().GetServerId(), "tag a", 11, 110); 3849 mock_server_->AddUpdatePref(ids_.FromNumber(3).GetServerId(), 3850 ids_.root().GetServerId(), "tag a", 12, 120); 3851 mock_server_->AddUpdatePref(ids_.FromNumber(4).GetServerId(), 3852 ids_.root().GetServerId(), "tag a", 13, 130); 3853 3854 mock_server_->AddUpdatePref(ids_.FromNumber(105).GetServerId(), 3855 ids_.root().GetServerId(), "tag b", 14, 140); 3856 mock_server_->AddUpdatePref(ids_.FromNumber(102).GetServerId(), 3857 ids_.root().GetServerId(), "tag b", 15, 150); 3858 // Least ID: winner. 3859 mock_server_->AddUpdatePref(ids_.FromNumber(101).GetServerId(), 3860 ids_.root().GetServerId(), "tag b", 16, 160); 3861 mock_server_->AddUpdatePref(ids_.FromNumber(104).GetServerId(), 3862 ids_.root().GetServerId(), "tag b", 17, 170); 3863 3864 mock_server_->AddUpdatePref(ids_.FromNumber(205).GetServerId(), 3865 ids_.root().GetServerId(), "tag c", 18, 180); 3866 mock_server_->AddUpdatePref(ids_.FromNumber(202).GetServerId(), 3867 ids_.root().GetServerId(), "tag c", 19, 190); 3868 mock_server_->AddUpdatePref(ids_.FromNumber(204).GetServerId(), 3869 ids_.root().GetServerId(), "tag c", 20, 200); 3870 // Least ID: winner. 3871 mock_server_->AddUpdatePref(ids_.FromNumber(201).GetServerId(), 3872 ids_.root().GetServerId(), "tag c", 21, 210); 3873 3874 mock_server_->set_conflict_all_commits(true); 3875 3876 SyncShareNudge(); 3877 // This should cause client tag overwrite. 3878 { 3879 syncable::ReadTransaction trans(FROM_HERE, directory()); 3880 3881 Entry tag_a(&trans, GET_BY_CLIENT_TAG, "tag a"); 3882 ASSERT_TRUE(tag_a.good()); 3883 EXPECT_TRUE(tag_a.GetId().ServerKnows()); 3884 EXPECT_EQ(ids_.FromNumber(1), tag_a.GetId()); 3885 EXPECT_FALSE(tag_a.GetIsDel()); 3886 EXPECT_FALSE(tag_a.GetIsUnappliedUpdate()); 3887 EXPECT_FALSE(tag_a.GetIsUnsynced()); 3888 EXPECT_EQ(1, tag_a.GetBaseVersion()); 3889 EXPECT_EQ("tag a", tag_a.GetUniqueClientTag()); 3890 3891 Entry tag_b(&trans, GET_BY_CLIENT_TAG, "tag b"); 3892 ASSERT_TRUE(tag_b.good()); 3893 EXPECT_TRUE(tag_b.GetId().ServerKnows()); 3894 EXPECT_EQ(ids_.FromNumber(101), tag_b.GetId()); 3895 EXPECT_FALSE(tag_b.GetIsDel()); 3896 EXPECT_FALSE(tag_b.GetIsUnappliedUpdate()); 3897 EXPECT_FALSE(tag_b.GetIsUnsynced()); 3898 EXPECT_EQ(16, tag_b.GetBaseVersion()); 3899 EXPECT_EQ("tag b", tag_b.GetUniqueClientTag()); 3900 3901 Entry tag_c(&trans, GET_BY_CLIENT_TAG, "tag c"); 3902 ASSERT_TRUE(tag_c.good()); 3903 EXPECT_TRUE(tag_c.GetId().ServerKnows()); 3904 EXPECT_EQ(ids_.FromNumber(201), tag_c.GetId()); 3905 EXPECT_FALSE(tag_c.GetIsDel()); 3906 EXPECT_FALSE(tag_c.GetIsUnappliedUpdate()); 3907 EXPECT_FALSE(tag_c.GetIsUnsynced()); 3908 EXPECT_EQ(21, tag_c.GetBaseVersion()); 3909 EXPECT_EQ("tag c", tag_c.GetUniqueClientTag()); 3910 3911 syncable::Directory::Metahandles children; 3912 directory()->GetChildHandlesById(&trans, trans.root_id(), &children); 3913 ASSERT_EQ(3U, children.size()); 3914 } 3915} 3916 3917TEST_F(SyncerTest, UniqueServerTagUpdates) { 3918 // As a hurdle, introduce an item whose name is the same as the tag value 3919 // we'll use later. 3920 int64 hurdle_handle = CreateUnsyncedDirectory("bob", "id_bob"); 3921 { 3922 syncable::ReadTransaction trans(FROM_HERE, directory()); 3923 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle); 3924 ASSERT_TRUE(hurdle.good()); 3925 ASSERT_TRUE(!hurdle.GetIsDel()); 3926 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty()); 3927 ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob"); 3928 3929 // Try to lookup by the tagname. These should fail. 3930 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha"); 3931 EXPECT_FALSE(tag_alpha.good()); 3932 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob"); 3933 EXPECT_FALSE(tag_bob.good()); 3934 } 3935 3936 // Now download some tagged items as updates. 3937 mock_server_->AddUpdateDirectory( 3938 1, 0, "update1", 1, 10, std::string(), std::string()); 3939 mock_server_->SetLastUpdateServerTag("alpha"); 3940 mock_server_->AddUpdateDirectory( 3941 2, 0, "update2", 2, 20, std::string(), std::string()); 3942 mock_server_->SetLastUpdateServerTag("bob"); 3943 SyncShareNudge(); 3944 3945 { 3946 syncable::ReadTransaction trans(FROM_HERE, directory()); 3947 3948 // The new items should be applied as new entries, and we should be able 3949 // to look them up by their tag values. 3950 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha"); 3951 ASSERT_TRUE(tag_alpha.good()); 3952 ASSERT_TRUE(!tag_alpha.GetIsDel()); 3953 ASSERT_TRUE(tag_alpha.GetUniqueServerTag()== "alpha"); 3954 ASSERT_TRUE(tag_alpha.GetNonUniqueName()== "update1"); 3955 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob"); 3956 ASSERT_TRUE(tag_bob.good()); 3957 ASSERT_TRUE(!tag_bob.GetIsDel()); 3958 ASSERT_TRUE(tag_bob.GetUniqueServerTag()== "bob"); 3959 ASSERT_TRUE(tag_bob.GetNonUniqueName()== "update2"); 3960 // The old item should be unchanged. 3961 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle); 3962 ASSERT_TRUE(hurdle.good()); 3963 ASSERT_TRUE(!hurdle.GetIsDel()); 3964 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty()); 3965 ASSERT_TRUE(hurdle.GetNonUniqueName()== "bob"); 3966 } 3967} 3968 3969TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) { 3970 // The expectations of this test happen in the MockConnectionManager's 3971 // GetUpdates handler. EnableDatatype sets the expectation value from our 3972 // set of enabled/disabled datatypes. 3973 EnableDatatype(BOOKMARKS); 3974 SyncShareNudge(); 3975 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 3976 3977 EnableDatatype(AUTOFILL); 3978 SyncShareNudge(); 3979 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 3980 3981 EnableDatatype(PREFERENCES); 3982 SyncShareNudge(); 3983 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 3984 3985 DisableDatatype(BOOKMARKS); 3986 SyncShareNudge(); 3987 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 3988 3989 DisableDatatype(AUTOFILL); 3990 SyncShareNudge(); 3991 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 3992 3993 DisableDatatype(PREFERENCES); 3994 EnableDatatype(AUTOFILL); 3995 SyncShareNudge(); 3996 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 3997} 3998 3999// A typical scenario: server and client each have one update for the other. 4000// This is the "happy path" alternative to UpdateFailsThenDontCommit. 4001TEST_F(SyncerTest, UpdateThenCommit) { 4002 syncable::Id to_receive = ids_.NewServerId(); 4003 syncable::Id to_commit = ids_.NewLocalId(); 4004 4005 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10, 4006 foreign_cache_guid(), "-1"); 4007 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit); 4008 SyncShareNudge(); 4009 4010 // The sync cycle should have included a GetUpdate, then a commit. By the 4011 // time the commit happened, we should have known for sure that there were no 4012 // hierarchy conflicts, and reported this fact to the server. 4013 ASSERT_TRUE(mock_server_->last_request().has_commit()); 4014 VerifyNoHierarchyConflictsReported(mock_server_->last_request()); 4015 4016 syncable::ReadTransaction trans(FROM_HERE, directory()); 4017 4018 Entry received(&trans, GET_BY_ID, to_receive); 4019 ASSERT_TRUE(received.good()); 4020 EXPECT_FALSE(received.GetIsUnsynced()); 4021 EXPECT_FALSE(received.GetIsUnappliedUpdate()); 4022 4023 Entry committed(&trans, GET_BY_HANDLE, commit_handle); 4024 ASSERT_TRUE(committed.good()); 4025 EXPECT_FALSE(committed.GetIsUnsynced()); 4026 EXPECT_FALSE(committed.GetIsUnappliedUpdate()); 4027} 4028 4029// Same as above, but this time we fail to download updates. 4030// We should not attempt to commit anything unless we successfully downloaded 4031// updates, otherwise we risk causing a server-side conflict. 4032TEST_F(SyncerTest, UpdateFailsThenDontCommit) { 4033 syncable::Id to_receive = ids_.NewServerId(); 4034 syncable::Id to_commit = ids_.NewLocalId(); 4035 4036 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10, 4037 foreign_cache_guid(), "-1"); 4038 int64 commit_handle = CreateUnsyncedDirectory("y", to_commit); 4039 mock_server_->FailNextPostBufferToPathCall(); 4040 SyncShareNudge(); 4041 4042 syncable::ReadTransaction trans(FROM_HERE, directory()); 4043 4044 // We did not receive this update. 4045 Entry received(&trans, GET_BY_ID, to_receive); 4046 ASSERT_FALSE(received.good()); 4047 4048 // And our local update remains unapplied. 4049 Entry committed(&trans, GET_BY_HANDLE, commit_handle); 4050 ASSERT_TRUE(committed.good()); 4051 EXPECT_TRUE(committed.GetIsUnsynced()); 4052 EXPECT_FALSE(committed.GetIsUnappliedUpdate()); 4053 4054 // Inform the Mock we won't be fetching all updates. 4055 mock_server_->ClearUpdatesQueue(); 4056} 4057 4058// Downloads two updates and applies them successfully. 4059// This is the "happy path" alternative to ConfigureFailsDontApplyUpdates. 4060TEST_F(SyncerTest, ConfigureDownloadsTwoBatchesSuccess) { 4061 syncable::Id node1 = ids_.NewServerId(); 4062 syncable::Id node2 = ids_.NewServerId(); 4063 4064 // Construct the first GetUpdates response. 4065 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10, 4066 foreign_cache_guid(), "-2"); 4067 mock_server_->SetChangesRemaining(1); 4068 mock_server_->NextUpdateBatch(); 4069 4070 // Construct the second GetUpdates response. 4071 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20, 4072 foreign_cache_guid(), "-2"); 4073 4074 SyncShareConfigure(); 4075 4076 syncable::ReadTransaction trans(FROM_HERE, directory()); 4077 // Both nodes should be downloaded and applied. 4078 4079 Entry n1(&trans, GET_BY_ID, node1); 4080 ASSERT_TRUE(n1.good()); 4081 EXPECT_FALSE(n1.GetIsUnappliedUpdate()); 4082 4083 Entry n2(&trans, GET_BY_ID, node2); 4084 ASSERT_TRUE(n2.good()); 4085 EXPECT_FALSE(n2.GetIsUnappliedUpdate()); 4086} 4087 4088// Same as the above case, but this time the second batch fails to download. 4089TEST_F(SyncerTest, ConfigureFailsDontApplyUpdates) { 4090 syncable::Id node1 = ids_.NewServerId(); 4091 syncable::Id node2 = ids_.NewServerId(); 4092 4093 // The scenario: we have two batches of updates with one update each. A 4094 // normal confgure step would download all the updates one batch at a time and 4095 // apply them. This configure will succeed in downloading the first batch 4096 // then fail when downloading the second. 4097 mock_server_->FailNthPostBufferToPathCall(2); 4098 4099 // Construct the first GetUpdates response. 4100 mock_server_->AddUpdateDirectory(node1, ids_.root(), "one", 1, 10, 4101 foreign_cache_guid(), "-1"); 4102 mock_server_->SetChangesRemaining(1); 4103 mock_server_->NextUpdateBatch(); 4104 4105 // Consutrct the second GetUpdates response. 4106 mock_server_->AddUpdateDirectory(node2, ids_.root(), "two", 1, 20, 4107 foreign_cache_guid(), "-2"); 4108 4109 SyncShareConfigure(); 4110 4111 syncable::ReadTransaction trans(FROM_HERE, directory()); 4112 4113 // The first node was downloaded, but not applied. 4114 Entry n1(&trans, GET_BY_ID, node1); 4115 ASSERT_TRUE(n1.good()); 4116 EXPECT_TRUE(n1.GetIsUnappliedUpdate()); 4117 4118 // The second node was not downloaded. 4119 Entry n2(&trans, GET_BY_ID, node2); 4120 EXPECT_FALSE(n2.good()); 4121 4122 // One update remains undownloaded. 4123 mock_server_->ClearUpdatesQueue(); 4124} 4125 4126TEST_F(SyncerTest, GetKeySuccess) { 4127 { 4128 syncable::ReadTransaction rtrans(FROM_HERE, directory()); 4129 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans)); 4130 } 4131 4132 SyncShareConfigure(); 4133 4134 EXPECT_EQ(session_->status_controller().last_get_key_result(), SYNCER_OK); 4135 { 4136 syncable::ReadTransaction rtrans(FROM_HERE, directory()); 4137 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans)); 4138 } 4139} 4140 4141TEST_F(SyncerTest, GetKeyEmpty) { 4142 { 4143 syncable::ReadTransaction rtrans(FROM_HERE, directory()); 4144 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans)); 4145 } 4146 4147 mock_server_->SetKeystoreKey(std::string()); 4148 SyncShareConfigure(); 4149 4150 EXPECT_NE(session_->status_controller().last_get_key_result(), SYNCER_OK); 4151 { 4152 syncable::ReadTransaction rtrans(FROM_HERE, directory()); 4153 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans)); 4154 } 4155} 4156 4157// Test what happens if a client deletes, then recreates, an object very 4158// quickly. It is possible that the deletion gets sent as a commit, and 4159// the undelete happens during the commit request. The principle here 4160// is that with a single committing client, conflicts should never 4161// be encountered, and a client encountering its past actions during 4162// getupdates should never feed back to override later actions. 4163// 4164// In cases of ordering A-F below, the outcome should be the same. 4165// Exercised by UndeleteDuringCommit: 4166// A. Delete - commit - undelete - commitresponse. 4167// B. Delete - commit - undelete - commitresponse - getupdates. 4168// Exercised by UndeleteBeforeCommit: 4169// C. Delete - undelete - commit - commitresponse. 4170// D. Delete - undelete - commit - commitresponse - getupdates. 4171// Exercised by UndeleteAfterCommit: 4172// E. Delete - commit - commitresponse - undelete - commit 4173// - commitresponse. 4174// F. Delete - commit - commitresponse - undelete - commit - 4175// - commitresponse - getupdates. 4176class SyncerUndeletionTest : public SyncerTest { 4177 public: 4178 SyncerUndeletionTest() 4179 : client_tag_("foobar"), 4180 metahandle_(syncable::kInvalidMetaHandle) { 4181 } 4182 4183 void Create() { 4184 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 4185 MutableEntry perm_folder( 4186 &trans, CREATE, BOOKMARKS, ids_.root(), "clientname"); 4187 ASSERT_TRUE(perm_folder.good()); 4188 perm_folder.PutUniqueClientTag(client_tag_); 4189 perm_folder.PutIsUnsynced(true); 4190 perm_folder.PutSyncing(false); 4191 perm_folder.PutSpecifics(DefaultBookmarkSpecifics()); 4192 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); 4193 EXPECT_FALSE(perm_folder.GetId().ServerKnows()); 4194 metahandle_ = perm_folder.GetMetahandle(); 4195 local_id_ = perm_folder.GetId(); 4196 } 4197 4198 void Delete() { 4199 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 4200 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); 4201 ASSERT_TRUE(entry.good()); 4202 EXPECT_EQ(metahandle_, entry.GetMetahandle()); 4203 entry.PutIsDel(true); 4204 entry.PutIsUnsynced(true); 4205 entry.PutSyncing(false); 4206 } 4207 4208 void Undelete() { 4209 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 4210 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); 4211 ASSERT_TRUE(entry.good()); 4212 EXPECT_EQ(metahandle_, entry.GetMetahandle()); 4213 EXPECT_TRUE(entry.GetIsDel()); 4214 entry.PutIsDel(false); 4215 entry.PutIsUnsynced(true); 4216 entry.PutSyncing(false); 4217 } 4218 4219 int64 GetMetahandleOfTag() { 4220 syncable::ReadTransaction trans(FROM_HERE, directory()); 4221 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); 4222 EXPECT_TRUE(entry.good()); 4223 if (!entry.good()) { 4224 return syncable::kInvalidMetaHandle; 4225 } 4226 return entry.GetMetahandle(); 4227 } 4228 4229 void ExpectUnsyncedCreation() { 4230 syncable::ReadTransaction trans(FROM_HERE, directory()); 4231 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); 4232 4233 EXPECT_EQ(metahandle_, entry.GetMetahandle()); 4234 EXPECT_FALSE(entry.GetIsDel()); 4235 EXPECT_FALSE(entry.GetServerIsDel()); // Never been committed. 4236 EXPECT_GE(0, entry.GetBaseVersion()); 4237 EXPECT_TRUE(entry.GetIsUnsynced()); 4238 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 4239 } 4240 4241 void ExpectUnsyncedUndeletion() { 4242 syncable::ReadTransaction trans(FROM_HERE, directory()); 4243 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); 4244 4245 EXPECT_EQ(metahandle_, entry.GetMetahandle()); 4246 EXPECT_FALSE(entry.GetIsDel()); 4247 EXPECT_TRUE(entry.GetServerIsDel()); 4248 EXPECT_EQ(0, entry.GetBaseVersion()); 4249 EXPECT_TRUE(entry.GetIsUnsynced()); 4250 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 4251 EXPECT_TRUE(entry.GetId().ServerKnows()); 4252 } 4253 4254 void ExpectUnsyncedEdit() { 4255 syncable::ReadTransaction trans(FROM_HERE, directory()); 4256 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); 4257 4258 EXPECT_EQ(metahandle_, entry.GetMetahandle()); 4259 EXPECT_FALSE(entry.GetIsDel()); 4260 EXPECT_FALSE(entry.GetServerIsDel()); 4261 EXPECT_LT(0, entry.GetBaseVersion()); 4262 EXPECT_TRUE(entry.GetIsUnsynced()); 4263 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 4264 EXPECT_TRUE(entry.GetId().ServerKnows()); 4265 } 4266 4267 void ExpectUnsyncedDeletion() { 4268 syncable::ReadTransaction trans(FROM_HERE, directory()); 4269 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); 4270 4271 EXPECT_EQ(metahandle_, entry.GetMetahandle()); 4272 EXPECT_TRUE(entry.GetIsDel()); 4273 EXPECT_FALSE(entry.GetServerIsDel()); 4274 EXPECT_TRUE(entry.GetIsUnsynced()); 4275 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 4276 EXPECT_LT(0, entry.GetBaseVersion()); 4277 EXPECT_LT(0, entry.GetServerVersion()); 4278 } 4279 4280 void ExpectSyncedAndCreated() { 4281 syncable::ReadTransaction trans(FROM_HERE, directory()); 4282 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); 4283 4284 EXPECT_EQ(metahandle_, entry.GetMetahandle()); 4285 EXPECT_FALSE(entry.GetIsDel()); 4286 EXPECT_FALSE(entry.GetServerIsDel()); 4287 EXPECT_LT(0, entry.GetBaseVersion()); 4288 EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion()); 4289 EXPECT_FALSE(entry.GetIsUnsynced()); 4290 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 4291 } 4292 4293 void ExpectSyncedAndDeleted() { 4294 syncable::ReadTransaction trans(FROM_HERE, directory()); 4295 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); 4296 4297 EXPECT_EQ(metahandle_, entry.GetMetahandle()); 4298 EXPECT_TRUE(entry.GetIsDel()); 4299 EXPECT_TRUE(entry.GetServerIsDel()); 4300 EXPECT_FALSE(entry.GetIsUnsynced()); 4301 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 4302 EXPECT_GE(0, entry.GetBaseVersion()); 4303 EXPECT_GE(0, entry.GetServerVersion()); 4304 } 4305 4306 protected: 4307 const std::string client_tag_; 4308 syncable::Id local_id_; 4309 int64 metahandle_; 4310}; 4311 4312TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) { 4313 Create(); 4314 ExpectUnsyncedCreation(); 4315 SyncShareNudge(); 4316 4317 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4318 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4319 ExpectSyncedAndCreated(); 4320 4321 // Delete, begin committing the delete, then undelete while committing. 4322 Delete(); 4323 ExpectUnsyncedDeletion(); 4324 mock_server_->SetMidCommitCallback( 4325 base::Bind(&SyncerUndeletionTest::Undelete, base::Unretained(this))); 4326 SyncShareNudge(); 4327 4328 // We will continue to commit until all nodes are synced, so we expect 4329 // that both the delete and following undelete were committed. We haven't 4330 // downloaded any updates, though, so the SERVER fields will be the same 4331 // as they were at the start of the cycle. 4332 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4333 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4334 4335 { 4336 syncable::ReadTransaction trans(FROM_HERE, directory()); 4337 Entry entry(&trans, GET_BY_HANDLE, metahandle_); 4338 4339 // Server fields lag behind. 4340 EXPECT_FALSE(entry.GetServerIsDel()); 4341 4342 // We have committed the second (undelete) update. 4343 EXPECT_FALSE(entry.GetIsDel()); 4344 EXPECT_FALSE(entry.GetIsUnsynced()); 4345 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); 4346 } 4347 4348 // Now, encounter a GetUpdates corresponding to the deletion from 4349 // the server. The undeletion should prevail again and be committed. 4350 // None of this should trigger any conflict detection -- it is perfectly 4351 // normal to recieve updates from our own commits. 4352 mock_server_->SetMidCommitCallback(base::Closure()); 4353 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); 4354 update->set_originator_cache_guid(local_cache_guid()); 4355 update->set_originator_client_item_id(local_id_.GetServerId()); 4356 4357 SyncShareNudge(); 4358 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4359 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4360 ExpectSyncedAndCreated(); 4361} 4362 4363TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) { 4364 Create(); 4365 ExpectUnsyncedCreation(); 4366 SyncShareNudge(); 4367 4368 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4369 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4370 ExpectSyncedAndCreated(); 4371 4372 // Delete and undelete, then sync to pick up the result. 4373 Delete(); 4374 ExpectUnsyncedDeletion(); 4375 Undelete(); 4376 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists. 4377 SyncShareNudge(); 4378 4379 // The item ought to have committed successfully. 4380 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4381 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4382 ExpectSyncedAndCreated(); 4383 { 4384 syncable::ReadTransaction trans(FROM_HERE, directory()); 4385 Entry entry(&trans, GET_BY_HANDLE, metahandle_); 4386 EXPECT_EQ(2, entry.GetBaseVersion()); 4387 } 4388 4389 // Now, encounter a GetUpdates corresponding to the just-committed 4390 // update. 4391 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); 4392 update->set_originator_cache_guid(local_cache_guid()); 4393 update->set_originator_client_item_id(local_id_.GetServerId()); 4394 SyncShareNudge(); 4395 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4396 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4397 ExpectSyncedAndCreated(); 4398} 4399 4400TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) { 4401 Create(); 4402 ExpectUnsyncedCreation(); 4403 SyncShareNudge(); 4404 4405 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4406 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4407 ExpectSyncedAndCreated(); 4408 4409 // Delete and commit. 4410 Delete(); 4411 ExpectUnsyncedDeletion(); 4412 SyncShareNudge(); 4413 4414 // The item ought to have committed successfully. 4415 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4416 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4417 ExpectSyncedAndDeleted(); 4418 4419 // Before the GetUpdates, the item is locally undeleted. 4420 Undelete(); 4421 ExpectUnsyncedUndeletion(); 4422 4423 // Now, encounter a GetUpdates corresponding to the just-committed 4424 // deletion update. The undeletion should prevail. 4425 mock_server_->AddUpdateFromLastCommit(); 4426 SyncShareNudge(); 4427 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4428 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4429 ExpectSyncedAndCreated(); 4430} 4431 4432TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) { 4433 Create(); 4434 ExpectUnsyncedCreation(); 4435 SyncShareNudge(); 4436 4437 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4438 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4439 ExpectSyncedAndCreated(); 4440 4441 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); 4442 update->set_originator_cache_guid(local_cache_guid()); 4443 update->set_originator_client_item_id(local_id_.GetServerId()); 4444 SyncShareNudge(); 4445 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4446 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4447 ExpectSyncedAndCreated(); 4448 4449 // Delete and commit. 4450 Delete(); 4451 ExpectUnsyncedDeletion(); 4452 SyncShareNudge(); 4453 4454 // The item ought to have committed successfully. 4455 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4456 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4457 ExpectSyncedAndDeleted(); 4458 4459 // Now, encounter a GetUpdates corresponding to the just-committed 4460 // deletion update. Should be consistent. 4461 mock_server_->AddUpdateFromLastCommit(); 4462 SyncShareNudge(); 4463 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4464 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4465 ExpectSyncedAndDeleted(); 4466 4467 // After the GetUpdates, the item is locally undeleted. 4468 Undelete(); 4469 ExpectUnsyncedUndeletion(); 4470 4471 // Now, encounter a GetUpdates corresponding to the just-committed 4472 // deletion update. The undeletion should prevail. 4473 SyncShareNudge(); 4474 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4475 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4476 ExpectSyncedAndCreated(); 4477} 4478 4479// Test processing of undeletion GetUpdateses. 4480TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) { 4481 Create(); 4482 ExpectUnsyncedCreation(); 4483 SyncShareNudge(); 4484 4485 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4486 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4487 ExpectSyncedAndCreated(); 4488 4489 // Add a delete from the server. 4490 sync_pb::SyncEntity* update1 = mock_server_->AddUpdateFromLastCommit(); 4491 update1->set_originator_cache_guid(local_cache_guid()); 4492 update1->set_originator_client_item_id(local_id_.GetServerId()); 4493 SyncShareNudge(); 4494 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4495 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4496 ExpectSyncedAndCreated(); 4497 4498 // Some other client deletes the item. 4499 { 4500 syncable::ReadTransaction trans(FROM_HERE, directory()); 4501 Entry entry(&trans, GET_BY_HANDLE, metahandle_); 4502 mock_server_->AddUpdateTombstone(entry.GetId()); 4503 } 4504 SyncShareNudge(); 4505 4506 // The update ought to have applied successfully. 4507 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4508 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4509 ExpectSyncedAndDeleted(); 4510 4511 // Undelete it locally. 4512 Undelete(); 4513 ExpectUnsyncedUndeletion(); 4514 SyncShareNudge(); 4515 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4516 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4517 ExpectSyncedAndCreated(); 4518 4519 // Now, encounter a GetUpdates corresponding to the just-committed 4520 // deletion update. The undeletion should prevail. 4521 sync_pb::SyncEntity* update2 = mock_server_->AddUpdateFromLastCommit(); 4522 update2->set_originator_cache_guid(local_cache_guid()); 4523 update2->set_originator_client_item_id(local_id_.GetServerId()); 4524 SyncShareNudge(); 4525 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4526 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4527 ExpectSyncedAndCreated(); 4528} 4529 4530TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) { 4531 Create(); 4532 ExpectUnsyncedCreation(); 4533 SyncShareNudge(); 4534 4535 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4536 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4537 ExpectSyncedAndCreated(); 4538 4539 // Some other client deletes the item before we get a chance 4540 // to GetUpdates our original request. 4541 { 4542 syncable::ReadTransaction trans(FROM_HERE, directory()); 4543 Entry entry(&trans, GET_BY_HANDLE, metahandle_); 4544 mock_server_->AddUpdateTombstone(entry.GetId()); 4545 } 4546 SyncShareNudge(); 4547 4548 // The update ought to have applied successfully. 4549 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4550 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4551 ExpectSyncedAndDeleted(); 4552 4553 // Undelete it locally. 4554 Undelete(); 4555 ExpectUnsyncedUndeletion(); 4556 SyncShareNudge(); 4557 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4558 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4559 ExpectSyncedAndCreated(); 4560 4561 // Now, encounter a GetUpdates corresponding to the just-committed 4562 // deletion update. The undeletion should prevail. 4563 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); 4564 update->set_originator_cache_guid(local_cache_guid()); 4565 update->set_originator_client_item_id(local_id_.GetServerId()); 4566 SyncShareNudge(); 4567 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4568 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4569 ExpectSyncedAndCreated(); 4570} 4571 4572TEST_F(SyncerUndeletionTest, OtherClientUndeletes) { 4573 Create(); 4574 ExpectUnsyncedCreation(); 4575 SyncShareNudge(); 4576 4577 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4578 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4579 ExpectSyncedAndCreated(); 4580 4581 // Get the updates of our just-committed entry. 4582 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); 4583 update->set_originator_cache_guid(local_cache_guid()); 4584 update->set_originator_client_item_id(local_id_.GetServerId()); 4585 SyncShareNudge(); 4586 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4587 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4588 ExpectSyncedAndCreated(); 4589 4590 // We delete the item. 4591 Delete(); 4592 ExpectUnsyncedDeletion(); 4593 SyncShareNudge(); 4594 4595 // The update ought to have applied successfully. 4596 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4597 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4598 ExpectSyncedAndDeleted(); 4599 4600 // Now, encounter a GetUpdates corresponding to the just-committed 4601 // deletion update. 4602 mock_server_->AddUpdateFromLastCommit(); 4603 SyncShareNudge(); 4604 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4605 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4606 ExpectSyncedAndDeleted(); 4607 4608 // Some other client undeletes the item. 4609 { 4610 syncable::ReadTransaction trans(FROM_HERE, directory()); 4611 Entry entry(&trans, GET_BY_HANDLE, metahandle_); 4612 mock_server_->AddUpdateBookmark( 4613 entry.GetId(), 4614 entry.GetParentId(), 4615 "Thadeusz", 100, 1000, 4616 local_cache_guid(), local_id_.GetServerId()); 4617 } 4618 mock_server_->SetLastUpdateClientTag(client_tag_); 4619 SyncShareNudge(); 4620 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4621 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4622 ExpectSyncedAndCreated(); 4623 { 4624 syncable::ReadTransaction trans(FROM_HERE, directory()); 4625 Entry entry(&trans, GET_BY_HANDLE, metahandle_); 4626 EXPECT_EQ("Thadeusz", entry.GetNonUniqueName()); 4627 } 4628} 4629 4630TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) { 4631 Create(); 4632 ExpectUnsyncedCreation(); 4633 SyncShareNudge(); 4634 4635 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4636 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4637 ExpectSyncedAndCreated(); 4638 4639 // Get the updates of our just-committed entry. 4640 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); 4641 update->set_originator_cache_guid(local_cache_guid()); 4642 { 4643 syncable::ReadTransaction trans(FROM_HERE, directory()); 4644 Entry entry(&trans, GET_BY_HANDLE, metahandle_); 4645 update->set_originator_client_item_id(local_id_.GetServerId()); 4646 } 4647 SyncShareNudge(); 4648 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4649 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4650 ExpectSyncedAndCreated(); 4651 4652 // We delete the item. 4653 Delete(); 4654 ExpectUnsyncedDeletion(); 4655 SyncShareNudge(); 4656 4657 // The update ought to have applied successfully. 4658 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4659 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4660 ExpectSyncedAndDeleted(); 4661 4662 // Some other client undeletes before we see the update from our 4663 // commit. 4664 { 4665 syncable::ReadTransaction trans(FROM_HERE, directory()); 4666 Entry entry(&trans, GET_BY_HANDLE, metahandle_); 4667 mock_server_->AddUpdateBookmark( 4668 entry.GetId(), 4669 entry.GetParentId(), 4670 "Thadeusz", 100, 1000, 4671 local_cache_guid(), local_id_.GetServerId()); 4672 } 4673 mock_server_->SetLastUpdateClientTag(client_tag_); 4674 SyncShareNudge(); 4675 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); 4676 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); 4677 ExpectSyncedAndCreated(); 4678 { 4679 syncable::ReadTransaction trans(FROM_HERE, directory()); 4680 Entry entry(&trans, GET_BY_HANDLE, metahandle_); 4681 EXPECT_EQ("Thadeusz", entry.GetNonUniqueName()); 4682 } 4683} 4684 4685enum { 4686 TEST_PARAM_BOOKMARK_ENABLE_BIT, 4687 TEST_PARAM_AUTOFILL_ENABLE_BIT, 4688 TEST_PARAM_BIT_COUNT 4689}; 4690 4691class MixedResult : 4692 public SyncerTest, 4693 public ::testing::WithParamInterface<int> { 4694 protected: 4695 bool ShouldFailBookmarkCommit() { 4696 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT)) == 0; 4697 } 4698 bool ShouldFailAutofillCommit() { 4699 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT)) == 0; 4700 } 4701}; 4702 4703INSTANTIATE_TEST_CASE_P(ExtensionsActivity, 4704 MixedResult, 4705 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT)); 4706 4707TEST_P(MixedResult, ExtensionsActivity) { 4708 { 4709 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); 4710 4711 MutableEntry pref(&wtrans, CREATE, PREFERENCES, wtrans.root_id(), "pref"); 4712 ASSERT_TRUE(pref.good()); 4713 pref.PutIsUnsynced(true); 4714 4715 MutableEntry bookmark( 4716 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bookmark"); 4717 ASSERT_TRUE(bookmark.good()); 4718 bookmark.PutIsUnsynced(true); 4719 4720 if (ShouldFailBookmarkCommit()) { 4721 mock_server_->SetTransientErrorId(bookmark.GetId()); 4722 } 4723 4724 if (ShouldFailAutofillCommit()) { 4725 mock_server_->SetTransientErrorId(pref.GetId()); 4726 } 4727 } 4728 4729 4730 // Put some extenions activity records into the monitor. 4731 { 4732 ExtensionsActivity::Records records; 4733 records["ABC"].extension_id = "ABC"; 4734 records["ABC"].bookmark_write_count = 2049U; 4735 records["xyz"].extension_id = "xyz"; 4736 records["xyz"].bookmark_write_count = 4U; 4737 context_->extensions_activity()->PutRecords(records); 4738 } 4739 4740 SyncShareNudge(); 4741 4742 ExtensionsActivity::Records final_monitor_records; 4743 context_->extensions_activity()->GetAndClearRecords(&final_monitor_records); 4744 if (ShouldFailBookmarkCommit()) { 4745 ASSERT_EQ(2U, final_monitor_records.size()) 4746 << "Should restore records after unsuccessful bookmark commit."; 4747 EXPECT_EQ("ABC", final_monitor_records["ABC"].extension_id); 4748 EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id); 4749 EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count); 4750 EXPECT_EQ(4U, final_monitor_records["xyz"].bookmark_write_count); 4751 } else { 4752 EXPECT_TRUE(final_monitor_records.empty()) 4753 << "Should not restore records after successful bookmark commit."; 4754 } 4755} 4756 4757} // namespace syncer 4758