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