|
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 |