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