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