OLD | NEW |
| (Empty) |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 // | |
5 // Syncer unit tests. Unfortunately a lot of these tests | |
6 // are outdated and need to be reworked and updated. | |
7 | |
8 #include "sync/engine/syncer.h" | |
9 | |
10 #include <stddef.h> | |
11 #include <stdint.h> | |
12 | |
13 #include <algorithm> | |
14 #include <limits> | |
15 #include <list> | |
16 #include <map> | |
17 #include <memory> | |
18 #include <set> | |
19 #include <string> | |
20 | |
21 #include "base/bind.h" | |
22 #include "base/bind_helpers.h" | |
23 #include "base/callback.h" | |
24 #include "base/compiler_specific.h" | |
25 #include "base/location.h" | |
26 #include "base/macros.h" | |
27 #include "base/message_loop/message_loop.h" | |
28 #include "base/strings/string_number_conversions.h" | |
29 #include "base/test/histogram_tester.h" | |
30 #include "base/time/time.h" | |
31 #include "build/build_config.h" | |
32 #include "sync/engine/backoff_delay_provider.h" | |
33 #include "sync/engine/get_commit_ids.h" | |
34 #include "sync/engine/net/server_connection_manager.h" | |
35 #include "sync/engine/sync_scheduler_impl.h" | |
36 #include "sync/engine/syncer_proto_util.h" | |
37 #include "sync/internal_api/public/base/cancelation_signal.h" | |
38 #include "sync/internal_api/public/base/model_type.h" | |
39 #include "sync/internal_api/public/engine/model_safe_worker.h" | |
40 #include "sync/internal_api/public/sessions/commit_counters.h" | |
41 #include "sync/internal_api/public/sessions/status_counters.h" | |
42 #include "sync/internal_api/public/sessions/update_counters.h" | |
43 #include "sync/protocol/bookmark_specifics.pb.h" | |
44 #include "sync/protocol/nigori_specifics.pb.h" | |
45 #include "sync/protocol/preference_specifics.pb.h" | |
46 #include "sync/protocol/sync.pb.h" | |
47 #include "sync/sessions/sync_session_context.h" | |
48 #include "sync/syncable/mutable_entry.h" | |
49 #include "sync/syncable/nigori_util.h" | |
50 #include "sync/syncable/syncable_delete_journal.h" | |
51 #include "sync/syncable/syncable_read_transaction.h" | |
52 #include "sync/syncable/syncable_util.h" | |
53 #include "sync/syncable/syncable_write_transaction.h" | |
54 #include "sync/test/engine/fake_model_worker.h" | |
55 #include "sync/test/engine/mock_connection_manager.h" | |
56 #include "sync/test/engine/mock_nudge_handler.h" | |
57 #include "sync/test/engine/test_directory_setter_upper.h" | |
58 #include "sync/test/engine/test_id_factory.h" | |
59 #include "sync/test/engine/test_syncable_utils.h" | |
60 #include "sync/test/fake_encryptor.h" | |
61 #include "sync/test/fake_sync_encryption_handler.h" | |
62 #include "sync/test/sessions/mock_debug_info_getter.h" | |
63 #include "sync/util/cryptographer.h" | |
64 #include "sync/util/extensions_activity.h" | |
65 #include "sync/util/time.h" | |
66 #include "testing/gmock/include/gmock/gmock.h" | |
67 #include "testing/gtest/include/gtest/gtest.h" | |
68 | |
69 using base::TimeDelta; | |
70 | |
71 using std::count; | |
72 using std::map; | |
73 using std::multimap; | |
74 using std::set; | |
75 using std::string; | |
76 using std::vector; | |
77 | |
78 namespace syncer { | |
79 | |
80 using syncable::BaseTransaction; | |
81 using syncable::CountEntriesWithName; | |
82 using syncable::Directory; | |
83 using syncable::Entry; | |
84 using syncable::GetFirstEntryWithName; | |
85 using syncable::GetOnlyEntryWithName; | |
86 using syncable::Id; | |
87 using syncable::kEncryptedString; | |
88 using syncable::MutableEntry; | |
89 using syncable::WriteTransaction; | |
90 | |
91 using syncable::CREATE; | |
92 using syncable::GET_BY_HANDLE; | |
93 using syncable::GET_BY_ID; | |
94 using syncable::GET_BY_CLIENT_TAG; | |
95 using syncable::GET_BY_SERVER_TAG; | |
96 using syncable::GET_TYPE_ROOT; | |
97 using syncable::UNITTEST; | |
98 | |
99 using sessions::MockDebugInfoGetter; | |
100 using sessions::StatusController; | |
101 using sessions::SyncSessionContext; | |
102 using sessions::SyncSession; | |
103 | |
104 namespace { | |
105 | |
106 // A helper to hold on to the counters emitted by the sync engine. | |
107 class TypeDebugInfoCache : public TypeDebugInfoObserver { | |
108 public: | |
109 TypeDebugInfoCache(); | |
110 ~TypeDebugInfoCache() override; | |
111 | |
112 CommitCounters GetLatestCommitCounters(ModelType type) const; | |
113 UpdateCounters GetLatestUpdateCounters(ModelType type) const; | |
114 StatusCounters GetLatestStatusCounters(ModelType type) const; | |
115 | |
116 // TypeDebugInfoObserver implementation. | |
117 void OnCommitCountersUpdated(syncer::ModelType type, | |
118 const CommitCounters& counters) override; | |
119 void OnUpdateCountersUpdated(syncer::ModelType type, | |
120 const UpdateCounters& counters) override; | |
121 void OnStatusCountersUpdated(syncer::ModelType type, | |
122 const StatusCounters& counters) override; | |
123 | |
124 private: | |
125 std::map<ModelType, CommitCounters> commit_counters_map_; | |
126 std::map<ModelType, UpdateCounters> update_counters_map_; | |
127 std::map<ModelType, StatusCounters> status_counters_map_; | |
128 }; | |
129 | |
130 TypeDebugInfoCache::TypeDebugInfoCache() {} | |
131 | |
132 TypeDebugInfoCache::~TypeDebugInfoCache() {} | |
133 | |
134 CommitCounters TypeDebugInfoCache::GetLatestCommitCounters( | |
135 ModelType type) const { | |
136 std::map<ModelType, CommitCounters>::const_iterator it = | |
137 commit_counters_map_.find(type); | |
138 if (it == commit_counters_map_.end()) { | |
139 return CommitCounters(); | |
140 } else { | |
141 return it->second; | |
142 } | |
143 } | |
144 | |
145 UpdateCounters TypeDebugInfoCache::GetLatestUpdateCounters( | |
146 ModelType type) const { | |
147 std::map<ModelType, UpdateCounters>::const_iterator it = | |
148 update_counters_map_.find(type); | |
149 if (it == update_counters_map_.end()) { | |
150 return UpdateCounters(); | |
151 } else { | |
152 return it->second; | |
153 } | |
154 } | |
155 | |
156 StatusCounters TypeDebugInfoCache::GetLatestStatusCounters( | |
157 ModelType type) const { | |
158 std::map<ModelType, StatusCounters>::const_iterator it = | |
159 status_counters_map_.find(type); | |
160 if (it == status_counters_map_.end()) { | |
161 return StatusCounters(); | |
162 } else { | |
163 return it->second; | |
164 } | |
165 } | |
166 | |
167 void TypeDebugInfoCache::OnCommitCountersUpdated( | |
168 syncer::ModelType type, | |
169 const CommitCounters& counters) { | |
170 commit_counters_map_[type] = counters; | |
171 } | |
172 | |
173 void TypeDebugInfoCache::OnUpdateCountersUpdated( | |
174 syncer::ModelType type, | |
175 const UpdateCounters& counters) { | |
176 update_counters_map_[type] = counters; | |
177 } | |
178 | |
179 void TypeDebugInfoCache::OnStatusCountersUpdated( | |
180 syncer::ModelType type, | |
181 const StatusCounters& counters) { | |
182 status_counters_map_[type] = counters; | |
183 } | |
184 | |
185 } // namespace | |
186 | |
187 class SyncerTest : public testing::Test, | |
188 public SyncSession::Delegate, | |
189 public SyncEngineEventListener { | |
190 protected: | |
191 SyncerTest() | |
192 : extensions_activity_(new ExtensionsActivity), | |
193 syncer_(NULL), | |
194 last_client_invalidation_hint_buffer_size_(10) { | |
195 } | |
196 | |
197 // SyncSession::Delegate implementation. | |
198 void OnThrottled(const base::TimeDelta& throttle_duration) override { | |
199 FAIL() << "Should not get silenced."; | |
200 } | |
201 void OnTypesThrottled(ModelTypeSet types, | |
202 const base::TimeDelta& throttle_duration) override { | |
203 scheduler_->OnTypesThrottled(types, throttle_duration); | |
204 } | |
205 bool IsCurrentlyThrottled() override { return false; } | |
206 void OnReceivedLongPollIntervalUpdate( | |
207 const base::TimeDelta& new_interval) override { | |
208 last_long_poll_interval_received_ = new_interval; | |
209 } | |
210 void OnReceivedShortPollIntervalUpdate( | |
211 const base::TimeDelta& new_interval) override { | |
212 last_short_poll_interval_received_ = new_interval; | |
213 } | |
214 void OnReceivedCustomNudgeDelays( | |
215 const std::map<ModelType, base::TimeDelta>& delay_map) override { | |
216 std::map<ModelType, base::TimeDelta>::const_iterator iter = | |
217 delay_map.find(SESSIONS); | |
218 if (iter != delay_map.end() && iter->second > base::TimeDelta()) | |
219 last_sessions_commit_delay_ = iter->second; | |
220 iter = delay_map.find(BOOKMARKS); | |
221 if (iter != delay_map.end() && iter->second > base::TimeDelta()) | |
222 last_bookmarks_commit_delay_ = iter->second; | |
223 } | |
224 void OnReceivedClientInvalidationHintBufferSize(int size) override { | |
225 last_client_invalidation_hint_buffer_size_ = size; | |
226 } | |
227 void OnReceivedGuRetryDelay(const base::TimeDelta& delay) override {} | |
228 void OnReceivedMigrationRequest(ModelTypeSet types) override {} | |
229 void OnProtocolEvent(const ProtocolEvent& event) override {} | |
230 void OnSyncProtocolError(const SyncProtocolError& error) override {} | |
231 | |
232 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) { | |
233 // We're just testing the sync engine here, so we shunt everything to | |
234 // the SyncerThread. Datatypes which aren't enabled aren't in the map. | |
235 for (ModelTypeSet::Iterator it = enabled_datatypes_.First(); | |
236 it.Good(); it.Inc()) { | |
237 (*out)[it.Get()] = GROUP_PASSIVE; | |
238 } | |
239 } | |
240 | |
241 void OnSyncCycleEvent(const SyncCycleEvent& event) override { | |
242 DVLOG(1) << "HandleSyncEngineEvent in unittest " << event.what_happened; | |
243 // we only test for entry-specific events, not status changed ones. | |
244 switch (event.what_happened) { | |
245 case SyncCycleEvent::SYNC_CYCLE_BEGIN: // Fall through. | |
246 case SyncCycleEvent::STATUS_CHANGED: | |
247 case SyncCycleEvent::SYNC_CYCLE_ENDED: | |
248 return; | |
249 default: | |
250 FAIL() << "Handling unknown error type in unit tests!!"; | |
251 } | |
252 } | |
253 | |
254 void OnActionableError(const SyncProtocolError& error) override {} | |
255 void OnRetryTimeChanged(base::Time retry_time) override {} | |
256 void OnThrottledTypesChanged(ModelTypeSet throttled_types) override {} | |
257 void OnMigrationRequested(ModelTypeSet types) override {} | |
258 | |
259 void ResetSession() { | |
260 session_.reset(SyncSession::Build(context_.get(), this)); | |
261 } | |
262 | |
263 bool SyncShareNudge() { | |
264 ResetSession(); | |
265 | |
266 // Pretend we've seen a local change, to make the nudge_tracker look normal. | |
267 nudge_tracker_.RecordLocalChange(ModelTypeSet(BOOKMARKS)); | |
268 | |
269 return syncer_->NormalSyncShare(context_->GetEnabledTypes(), | |
270 &nudge_tracker_, session_.get()); | |
271 } | |
272 | |
273 bool SyncShareConfigure() { | |
274 ResetSession(); | |
275 return syncer_->ConfigureSyncShare( | |
276 context_->GetEnabledTypes(), | |
277 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION, | |
278 session_.get()); | |
279 } | |
280 | |
281 void SetUp() override { | |
282 dir_maker_.SetUp(); | |
283 mock_server_.reset(new MockConnectionManager(directory(), | |
284 &cancelation_signal_)); | |
285 debug_info_getter_.reset(new MockDebugInfoGetter); | |
286 EnableDatatype(BOOKMARKS); | |
287 EnableDatatype(EXTENSIONS); | |
288 EnableDatatype(NIGORI); | |
289 EnableDatatype(PREFERENCES); | |
290 EnableDatatype(NIGORI); | |
291 workers_.push_back(scoped_refptr<ModelSafeWorker>( | |
292 new FakeModelWorker(GROUP_PASSIVE))); | |
293 std::vector<SyncEngineEventListener*> listeners; | |
294 listeners.push_back(this); | |
295 | |
296 ModelSafeRoutingInfo routing_info; | |
297 GetModelSafeRoutingInfo(&routing_info); | |
298 | |
299 model_type_registry_.reset( | |
300 new ModelTypeRegistry(workers_, directory(), &mock_nudge_handler_)); | |
301 model_type_registry_->RegisterDirectoryTypeDebugInfoObserver( | |
302 &debug_info_cache_); | |
303 | |
304 context_.reset(new SyncSessionContext( | |
305 mock_server_.get(), | |
306 directory(), | |
307 extensions_activity_.get(), | |
308 listeners, | |
309 debug_info_getter_.get(), | |
310 model_type_registry_.get(), | |
311 true, // enable keystore encryption | |
312 false, // force enable pre-commit GU avoidance experiment | |
313 "fake_invalidator_client_id")); | |
314 context_->SetRoutingInfo(routing_info); | |
315 syncer_ = new Syncer(&cancelation_signal_); | |
316 scheduler_.reset(new SyncSchedulerImpl( | |
317 "TestSyncScheduler", | |
318 BackoffDelayProvider::FromDefaults(), | |
319 context_.get(), | |
320 // scheduler_ owned syncer_ now and will manage the memory of syncer_ | |
321 syncer_)); | |
322 | |
323 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
324 syncable::Directory::Metahandles children; | |
325 directory()->GetChildHandlesById(&trans, trans.root_id(), &children); | |
326 ASSERT_EQ(0u, children.size()); | |
327 root_id_ = TestIdFactory::root(); | |
328 parent_id_ = ids_.MakeServer("parent id"); | |
329 child_id_ = ids_.MakeServer("child id"); | |
330 directory()->set_store_birthday(mock_server_->store_birthday()); | |
331 mock_server_->SetKeystoreKey("encryption_key"); | |
332 } | |
333 | |
334 void TearDown() override { | |
335 model_type_registry_->UnregisterDirectoryTypeDebugInfoObserver( | |
336 &debug_info_cache_); | |
337 mock_server_.reset(); | |
338 scheduler_.reset(); | |
339 dir_maker_.TearDown(); | |
340 } | |
341 | |
342 void WriteTestDataToEntry(WriteTransaction* trans, MutableEntry* entry) { | |
343 EXPECT_FALSE(entry->GetIsDir()); | |
344 EXPECT_FALSE(entry->GetIsDel()); | |
345 sync_pb::EntitySpecifics specifics; | |
346 specifics.mutable_bookmark()->set_url("http://demo/"); | |
347 specifics.mutable_bookmark()->set_favicon("PNG"); | |
348 entry->PutSpecifics(specifics); | |
349 entry->PutIsUnsynced(true); | |
350 } | |
351 void VerifyTestDataInEntry(BaseTransaction* trans, Entry* entry) { | |
352 EXPECT_FALSE(entry->GetIsDir()); | |
353 EXPECT_FALSE(entry->GetIsDel()); | |
354 VerifyTestBookmarkDataInEntry(entry); | |
355 } | |
356 void VerifyTestBookmarkDataInEntry(Entry* entry) { | |
357 const sync_pb::EntitySpecifics& specifics = entry->GetSpecifics(); | |
358 EXPECT_TRUE(specifics.has_bookmark()); | |
359 EXPECT_EQ("PNG", specifics.bookmark().favicon()); | |
360 EXPECT_EQ("http://demo/", specifics.bookmark().url()); | |
361 } | |
362 | |
363 void VerifyHierarchyConflictsReported( | |
364 const sync_pb::ClientToServerMessage& message) { | |
365 // Our request should have included a warning about hierarchy conflicts. | |
366 const sync_pb::ClientStatus& client_status = message.client_status(); | |
367 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected()); | |
368 EXPECT_TRUE(client_status.hierarchy_conflict_detected()); | |
369 } | |
370 | |
371 void VerifyNoHierarchyConflictsReported( | |
372 const sync_pb::ClientToServerMessage& message) { | |
373 // Our request should have reported no hierarchy conflicts detected. | |
374 const sync_pb::ClientStatus& client_status = message.client_status(); | |
375 EXPECT_TRUE(client_status.has_hierarchy_conflict_detected()); | |
376 EXPECT_FALSE(client_status.hierarchy_conflict_detected()); | |
377 } | |
378 | |
379 void VerifyHierarchyConflictsUnspecified( | |
380 const sync_pb::ClientToServerMessage& message) { | |
381 // Our request should have neither confirmed nor denied hierarchy conflicts. | |
382 const sync_pb::ClientStatus& client_status = message.client_status(); | |
383 EXPECT_FALSE(client_status.has_hierarchy_conflict_detected()); | |
384 } | |
385 | |
386 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() { | |
387 sync_pb::EntitySpecifics result; | |
388 AddDefaultFieldValue(BOOKMARKS, &result); | |
389 return result; | |
390 } | |
391 | |
392 sync_pb::EntitySpecifics DefaultPreferencesSpecifics() { | |
393 sync_pb::EntitySpecifics result; | |
394 AddDefaultFieldValue(PREFERENCES, &result); | |
395 return result; | |
396 } | |
397 // Enumeration of alterations to entries for commit ordering tests. | |
398 enum EntryFeature { | |
399 LIST_END = 0, // Denotes the end of the list of features from below. | |
400 SYNCED, // Items are unsynced by default | |
401 DELETED, | |
402 OLD_MTIME, | |
403 MOVED_FROM_ROOT, | |
404 }; | |
405 | |
406 struct CommitOrderingTest { | |
407 // expected commit index. | |
408 int commit_index; | |
409 // Details about the item | |
410 syncable::Id id; | |
411 syncable::Id parent_id; | |
412 EntryFeature features[10]; | |
413 | |
414 static CommitOrderingTest MakeLastCommitItem() { | |
415 CommitOrderingTest last_commit_item; | |
416 last_commit_item.commit_index = -1; | |
417 last_commit_item.id = TestIdFactory::root(); | |
418 return last_commit_item; | |
419 } | |
420 }; | |
421 | |
422 void RunCommitOrderingTest(CommitOrderingTest* test) { | |
423 map<int, syncable::Id> expected_positions; | |
424 { // Transaction scope. | |
425 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
426 while (!test->id.IsRoot()) { | |
427 if (test->commit_index >= 0) { | |
428 map<int, syncable::Id>::value_type entry(test->commit_index, | |
429 test->id); | |
430 bool double_position = !expected_positions.insert(entry).second; | |
431 ASSERT_FALSE(double_position) << "Two id's expected at one position"; | |
432 } | |
433 string utf8_name = test->id.GetServerId(); | |
434 string name(utf8_name.begin(), utf8_name.end()); | |
435 MutableEntry entry(&trans, CREATE, BOOKMARKS, test->parent_id, name); | |
436 | |
437 entry.PutId(test->id); | |
438 if (test->id.ServerKnows()) { | |
439 entry.PutBaseVersion(5); | |
440 entry.PutServerVersion(5); | |
441 entry.PutServerParentId(test->parent_id); | |
442 } | |
443 entry.PutIsDir(true); | |
444 entry.PutIsUnsynced(true); | |
445 entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
446 // Set the time to 30 seconds in the future to reduce the chance of | |
447 // flaky tests. | |
448 const base::Time& now_plus_30s = | |
449 base::Time::Now() + base::TimeDelta::FromSeconds(30); | |
450 const base::Time& now_minus_2h = | |
451 base::Time::Now() - base::TimeDelta::FromHours(2); | |
452 entry.PutMtime(now_plus_30s); | |
453 for (size_t i = 0 ; i < arraysize(test->features) ; ++i) { | |
454 switch (test->features[i]) { | |
455 case LIST_END: | |
456 break; | |
457 case SYNCED: | |
458 entry.PutIsUnsynced(false); | |
459 break; | |
460 case DELETED: | |
461 entry.PutIsDel(true); | |
462 break; | |
463 case OLD_MTIME: | |
464 entry.PutMtime(now_minus_2h); | |
465 break; | |
466 case MOVED_FROM_ROOT: | |
467 entry.PutServerParentId(trans.root_id()); | |
468 break; | |
469 default: | |
470 FAIL() << "Bad value in CommitOrderingTest list"; | |
471 } | |
472 } | |
473 test++; | |
474 } | |
475 } | |
476 EXPECT_TRUE(SyncShareNudge()); | |
477 ASSERT_TRUE(expected_positions.size() == | |
478 mock_server_->committed_ids().size()); | |
479 // If this test starts failing, be aware other sort orders could be valid. | |
480 for (size_t i = 0; i < expected_positions.size(); ++i) { | |
481 SCOPED_TRACE(i); | |
482 EXPECT_EQ(1u, expected_positions.count(i)); | |
483 EXPECT_EQ(expected_positions[i], mock_server_->committed_ids()[i]); | |
484 } | |
485 } | |
486 | |
487 CommitCounters GetCommitCounters(ModelType type) { | |
488 return debug_info_cache_.GetLatestCommitCounters(type); | |
489 } | |
490 | |
491 UpdateCounters GetUpdateCounters(ModelType type) { | |
492 return debug_info_cache_.GetLatestUpdateCounters(type); | |
493 } | |
494 | |
495 StatusCounters GetStatusCounters(ModelType type) { | |
496 return debug_info_cache_.GetLatestStatusCounters(type); | |
497 } | |
498 | |
499 Directory* directory() { | |
500 return dir_maker_.directory(); | |
501 } | |
502 | |
503 const std::string local_cache_guid() { | |
504 return directory()->cache_guid(); | |
505 } | |
506 | |
507 const std::string foreign_cache_guid() { | |
508 return "kqyg7097kro6GSUod+GSg=="; | |
509 } | |
510 | |
511 int64_t CreateUnsyncedDirectory(const string& entry_name, | |
512 const string& idstring) { | |
513 return CreateUnsyncedDirectory(entry_name, | |
514 syncable::Id::CreateFromServerId(idstring)); | |
515 } | |
516 | |
517 int64_t CreateUnsyncedDirectory(const string& entry_name, | |
518 const syncable::Id& id) { | |
519 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
520 MutableEntry entry( | |
521 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), entry_name); | |
522 EXPECT_TRUE(entry.good()); | |
523 entry.PutIsUnsynced(true); | |
524 entry.PutIsDir(true); | |
525 entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
526 entry.PutBaseVersion(id.ServerKnows() ? 1 : 0); | |
527 entry.PutId(id); | |
528 return entry.GetMetahandle(); | |
529 } | |
530 | |
531 void EnableDatatype(ModelType model_type) { | |
532 enabled_datatypes_.Put(model_type); | |
533 | |
534 ModelSafeRoutingInfo routing_info; | |
535 GetModelSafeRoutingInfo(&routing_info); | |
536 | |
537 if (context_) { | |
538 context_->SetRoutingInfo(routing_info); | |
539 } | |
540 | |
541 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_); | |
542 } | |
543 | |
544 void DisableDatatype(ModelType model_type) { | |
545 enabled_datatypes_.Remove(model_type); | |
546 | |
547 ModelSafeRoutingInfo routing_info; | |
548 GetModelSafeRoutingInfo(&routing_info); | |
549 | |
550 if (context_) { | |
551 context_->SetRoutingInfo(routing_info); | |
552 } | |
553 | |
554 mock_server_->ExpectGetUpdatesRequestTypes(enabled_datatypes_); | |
555 } | |
556 | |
557 Cryptographer* GetCryptographer(syncable::BaseTransaction* trans) { | |
558 return directory()->GetCryptographer(trans); | |
559 } | |
560 | |
561 // Configures SyncSessionContext and NudgeTracker so Syncer won't call | |
562 // GetUpdates prior to Commit. This method can be used to ensure a Commit is | |
563 // not preceeded by GetUpdates. | |
564 void ConfigureNoGetUpdatesRequired() { | |
565 context_->set_server_enabled_pre_commit_update_avoidance(true); | |
566 nudge_tracker_.OnInvalidationsEnabled(); | |
567 nudge_tracker_.RecordSuccessfulSyncCycle(); | |
568 | |
569 ASSERT_FALSE(context_->ShouldFetchUpdatesBeforeCommit()); | |
570 ASSERT_FALSE(nudge_tracker_.IsGetUpdatesRequired()); | |
571 } | |
572 | |
573 base::MessageLoop message_loop_; | |
574 | |
575 // Some ids to aid tests. Only the root one's value is specific. The rest | |
576 // are named for test clarity. | |
577 // TODO(chron): Get rid of these inbuilt IDs. They only make it | |
578 // more confusing. | |
579 syncable::Id root_id_; | |
580 syncable::Id parent_id_; | |
581 syncable::Id child_id_; | |
582 | |
583 TestIdFactory ids_; | |
584 | |
585 TestDirectorySetterUpper dir_maker_; | |
586 FakeEncryptor encryptor_; | |
587 scoped_refptr<ExtensionsActivity> extensions_activity_; | |
588 std::unique_ptr<MockConnectionManager> mock_server_; | |
589 CancelationSignal cancelation_signal_; | |
590 | |
591 Syncer* syncer_; | |
592 | |
593 std::unique_ptr<SyncSession> session_; | |
594 TypeDebugInfoCache debug_info_cache_; | |
595 MockNudgeHandler mock_nudge_handler_; | |
596 std::unique_ptr<ModelTypeRegistry> model_type_registry_; | |
597 std::unique_ptr<SyncSchedulerImpl> scheduler_; | |
598 std::unique_ptr<SyncSessionContext> context_; | |
599 base::TimeDelta last_short_poll_interval_received_; | |
600 base::TimeDelta last_long_poll_interval_received_; | |
601 base::TimeDelta last_sessions_commit_delay_; | |
602 base::TimeDelta last_bookmarks_commit_delay_; | |
603 int last_client_invalidation_hint_buffer_size_; | |
604 std::vector<scoped_refptr<ModelSafeWorker> > workers_; | |
605 | |
606 ModelTypeSet enabled_datatypes_; | |
607 sessions::NudgeTracker nudge_tracker_; | |
608 std::unique_ptr<MockDebugInfoGetter> debug_info_getter_; | |
609 | |
610 private: | |
611 DISALLOW_COPY_AND_ASSIGN(SyncerTest); | |
612 }; | |
613 | |
614 TEST_F(SyncerTest, TestCallGatherUnsyncedEntries) { | |
615 { | |
616 Syncer::UnsyncedMetaHandles handles; | |
617 { | |
618 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
619 GetUnsyncedEntries(&trans, &handles); | |
620 } | |
621 ASSERT_EQ(0u, handles.size()); | |
622 } | |
623 // TODO(sync): When we can dynamically connect and disconnect the mock | |
624 // ServerConnectionManager test disconnected GetUnsyncedEntries here. It's a | |
625 // regression for a very old bug. | |
626 } | |
627 | |
628 TEST_F(SyncerTest, GetCommitIdsFiltersThrottledEntries) { | |
629 const ModelTypeSet throttled_types(BOOKMARKS); | |
630 sync_pb::EntitySpecifics bookmark_data; | |
631 AddDefaultFieldValue(BOOKMARKS, &bookmark_data); | |
632 | |
633 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10, | |
634 foreign_cache_guid(), "-1"); | |
635 EXPECT_TRUE(SyncShareNudge()); | |
636 | |
637 { | |
638 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
639 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
640 ASSERT_TRUE(A.good()); | |
641 A.PutIsUnsynced(true); | |
642 A.PutSpecifics(bookmark_data); | |
643 A.PutNonUniqueName("bookmark"); | |
644 } | |
645 | |
646 // Now sync without enabling bookmarks. | |
647 mock_server_->ExpectGetUpdatesRequestTypes( | |
648 Difference(context_->GetEnabledTypes(), ModelTypeSet(BOOKMARKS))); | |
649 ResetSession(); | |
650 syncer_->NormalSyncShare( | |
651 Difference(context_->GetEnabledTypes(), ModelTypeSet(BOOKMARKS)), | |
652 &nudge_tracker_, session_.get()); | |
653 | |
654 { | |
655 // Nothing should have been committed as bookmarks is throttled. | |
656 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
657 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1)); | |
658 ASSERT_TRUE(entryA.good()); | |
659 EXPECT_TRUE(entryA.GetIsUnsynced()); | |
660 } | |
661 | |
662 // Sync again with bookmarks enabled. | |
663 mock_server_->ExpectGetUpdatesRequestTypes(context_->GetEnabledTypes()); | |
664 EXPECT_TRUE(SyncShareNudge()); | |
665 { | |
666 // It should have been committed. | |
667 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
668 Entry entryA(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1)); | |
669 ASSERT_TRUE(entryA.good()); | |
670 EXPECT_FALSE(entryA.GetIsUnsynced()); | |
671 } | |
672 } | |
673 | |
674 // This test has three steps. In the first step, a BOOKMARK update is received. | |
675 // In the next step, syncing BOOKMARKS is disabled, so no BOOKMARK is sent or | |
676 // received. In the last step, a BOOKMARK update is committed. | |
677 TEST_F(SyncerTest, DataUseHistogramsTest) { | |
678 base::HistogramTester histogram_tester; | |
679 sync_pb::EntitySpecifics bookmark_data; | |
680 AddDefaultFieldValue(BOOKMARKS, &bookmark_data); | |
681 | |
682 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10, foreign_cache_guid(), | |
683 "-1"); | |
684 int download_bytes_bookmark = 0; | |
685 vector<unsigned int> progress_bookmark(3, 0); | |
686 vector<unsigned int> progress_all(3, 0); | |
687 vector<base::Bucket> samples; | |
688 EXPECT_TRUE(SyncShareNudge()); | |
689 { | |
690 histogram_tester.ExpectTotalCount("DataUse.Sync.Upload.Count", 0); | |
691 histogram_tester.ExpectTotalCount("DataUse.Sync.Upload.Bytes", 0); | |
692 histogram_tester.ExpectTotalCount("DataUse.Sync.Download.Count", 1); | |
693 histogram_tester.ExpectUniqueSample("DataUse.Sync.Download.Count", | |
694 BOOKMARKS, 1); | |
695 samples = histogram_tester.GetAllSamples("DataUse.Sync.Download.Bytes"); | |
696 EXPECT_EQ(1u, samples.size()); | |
697 EXPECT_EQ(BOOKMARKS, samples.at(0).min); | |
698 EXPECT_GE(samples.at(0).count, 0); | |
699 download_bytes_bookmark = samples.at(0).count; | |
700 | |
701 samples = | |
702 histogram_tester.GetAllSamples("DataUse.Sync.ProgressMarker.Bytes"); | |
703 | |
704 for (const base::Bucket& bucket : samples) { | |
705 if (bucket.min == BOOKMARKS) | |
706 progress_bookmark.at(0) += bucket.count; | |
707 progress_all.at(0) += bucket.count; | |
708 } | |
709 EXPECT_GT(progress_bookmark.at(0), 0u); | |
710 EXPECT_GT(progress_all.at(0), 0u); | |
711 | |
712 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
713 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
714 A.PutIsUnsynced(true); | |
715 A.PutSpecifics(bookmark_data); | |
716 A.PutNonUniqueName("bookmark"); | |
717 } | |
718 | |
719 // Now sync without enabling bookmarks. | |
720 mock_server_->ExpectGetUpdatesRequestTypes( | |
721 Difference(context_->GetEnabledTypes(), ModelTypeSet(BOOKMARKS))); | |
722 ResetSession(); | |
723 syncer_->NormalSyncShare( | |
724 Difference(context_->GetEnabledTypes(), ModelTypeSet(BOOKMARKS)), | |
725 &nudge_tracker_, session_.get()); | |
726 | |
727 { | |
728 // Nothing should have been committed as bookmarks is throttled. | |
729 histogram_tester.ExpectTotalCount("DataUse.Sync.Upload.Count", 0); | |
730 histogram_tester.ExpectTotalCount("DataUse.Sync.Upload.Bytes", 0); | |
731 histogram_tester.ExpectTotalCount("DataUse.Sync.Download.Count", 1); | |
732 histogram_tester.ExpectUniqueSample("DataUse.Sync.Download.Count", | |
733 BOOKMARKS, 1); | |
734 | |
735 samples = histogram_tester.GetAllSamples("DataUse.Sync.Download.Bytes"); | |
736 EXPECT_EQ(1u, samples.size()); | |
737 EXPECT_EQ(BOOKMARKS, samples.at(0).min); | |
738 EXPECT_EQ(download_bytes_bookmark, samples.at(0).count); | |
739 | |
740 samples = | |
741 histogram_tester.GetAllSamples("DataUse.Sync.ProgressMarker.Bytes"); | |
742 for (const base::Bucket& bucket : samples) { | |
743 if (bucket.min == BOOKMARKS) | |
744 progress_bookmark.at(1) += bucket.count; | |
745 progress_all.at(1) += bucket.count; | |
746 } | |
747 EXPECT_EQ(progress_bookmark.at(1), progress_bookmark.at(0)); | |
748 EXPECT_GT(progress_all.at(1), progress_all.at(0)); | |
749 } | |
750 | |
751 // Sync again with bookmarks enabled. | |
752 mock_server_->ExpectGetUpdatesRequestTypes(context_->GetEnabledTypes()); | |
753 EXPECT_TRUE(SyncShareNudge()); | |
754 { | |
755 // It should have been committed. | |
756 histogram_tester.ExpectTotalCount("DataUse.Sync.Upload.Count", 1); | |
757 histogram_tester.ExpectUniqueSample("DataUse.Sync.Upload.Count", BOOKMARKS, | |
758 1); | |
759 samples = histogram_tester.GetAllSamples("DataUse.Sync.Upload.Bytes"); | |
760 EXPECT_EQ(1u, samples.size()); | |
761 EXPECT_EQ(BOOKMARKS, samples.at(0).min); | |
762 EXPECT_GE(samples.at(0).count, 0); | |
763 | |
764 samples = histogram_tester.GetAllSamples("DataUse.Sync.Download.Bytes"); | |
765 EXPECT_EQ(1u, samples.size()); | |
766 EXPECT_EQ(BOOKMARKS, samples.at(0).min); | |
767 EXPECT_EQ(download_bytes_bookmark, samples.at(0).count); | |
768 | |
769 histogram_tester.ExpectTotalCount("DataUse.Sync.Download.Count", 1); | |
770 | |
771 samples = | |
772 histogram_tester.GetAllSamples("DataUse.Sync.ProgressMarker.Bytes"); | |
773 for (const base::Bucket& bucket : samples) { | |
774 if (bucket.min == BOOKMARKS) | |
775 progress_bookmark.at(2) += bucket.count; | |
776 progress_all.at(2) += bucket.count; | |
777 } | |
778 EXPECT_GT(progress_bookmark.at(2), progress_bookmark.at(1)); | |
779 EXPECT_GT(progress_all.at(2), progress_all.at(1)); | |
780 } | |
781 } | |
782 | |
783 // We use a macro so we can preserve the error location. | |
784 #define VERIFY_ENTRY(id, is_unapplied, is_unsynced, prev_initialized, \ | |
785 parent_id, version, server_version, id_fac, rtrans) \ | |
786 do { \ | |
787 Entry entryA(rtrans, syncable::GET_BY_ID, id_fac.FromNumber(id)); \ | |
788 ASSERT_TRUE(entryA.good()); \ | |
789 /* We don't use EXPECT_EQ here because if the left side param is false,*/ \ | |
790 /* gcc 4.6 warns converting 'false' to pointer type for argument 1.*/ \ | |
791 EXPECT_TRUE(is_unsynced == entryA.GetIsUnsynced()); \ | |
792 EXPECT_TRUE(is_unapplied == entryA.GetIsUnappliedUpdate()); \ | |
793 EXPECT_TRUE(prev_initialized == IsRealDataType(GetModelTypeFromSpecifics( \ | |
794 entryA.GetBaseServerSpecifics()))); \ | |
795 EXPECT_TRUE(parent_id == -1 || \ | |
796 entryA.GetParentId() == id_fac.FromNumber(parent_id)); \ | |
797 EXPECT_EQ(version, entryA.GetBaseVersion()); \ | |
798 EXPECT_EQ(server_version, entryA.GetServerVersion()); \ | |
799 } while (0) | |
800 | |
801 TEST_F(SyncerTest, GetCommitIdsFiltersUnreadyEntries) { | |
802 KeyParams key_params = {"localhost", "dummy", "foobar"}; | |
803 KeyParams other_params = {"localhost", "dummy", "foobar2"}; | |
804 sync_pb::EntitySpecifics bookmark, encrypted_bookmark; | |
805 bookmark.mutable_bookmark()->set_url("url"); | |
806 bookmark.mutable_bookmark()->set_title("title"); | |
807 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark); | |
808 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10, | |
809 foreign_cache_guid(), "-1"); | |
810 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10, | |
811 foreign_cache_guid(), "-2"); | |
812 mock_server_->AddUpdateDirectory(3, 0, "C", 10, 10, | |
813 foreign_cache_guid(), "-3"); | |
814 mock_server_->AddUpdateDirectory(4, 0, "D", 10, 10, | |
815 foreign_cache_guid(), "-4"); | |
816 EXPECT_TRUE(SyncShareNudge()); | |
817 // Server side change will put A in conflict. | |
818 mock_server_->AddUpdateDirectory(1, 0, "A", 20, 20, | |
819 foreign_cache_guid(), "-1"); | |
820 { | |
821 // Mark bookmarks as encrypted and set the cryptographer to have pending | |
822 // keys. | |
823 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
824 Cryptographer other_cryptographer(&encryptor_); | |
825 other_cryptographer.AddKey(other_params); | |
826 sync_pb::EntitySpecifics specifics; | |
827 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori(); | |
828 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag()); | |
829 dir_maker_.encryption_handler()->EnableEncryptEverything(); | |
830 // Set up with an old passphrase, but have pending keys | |
831 GetCryptographer(&wtrans)->AddKey(key_params); | |
832 GetCryptographer(&wtrans)->Encrypt(bookmark, | |
833 encrypted_bookmark.mutable_encrypted()); | |
834 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag()); | |
835 | |
836 // In conflict but properly encrypted. | |
837 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
838 ASSERT_TRUE(A.good()); | |
839 A.PutIsUnsynced(true); | |
840 A.PutSpecifics(encrypted_bookmark); | |
841 A.PutNonUniqueName(kEncryptedString); | |
842 // Not in conflict and properly encrypted. | |
843 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); | |
844 ASSERT_TRUE(B.good()); | |
845 B.PutIsUnsynced(true); | |
846 B.PutSpecifics(encrypted_bookmark); | |
847 B.PutNonUniqueName(kEncryptedString); | |
848 // Unencrypted specifics. | |
849 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3)); | |
850 ASSERT_TRUE(C.good()); | |
851 C.PutIsUnsynced(true); | |
852 C.PutNonUniqueName(kEncryptedString); | |
853 // Unencrypted non_unique_name. | |
854 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4)); | |
855 ASSERT_TRUE(D.good()); | |
856 D.PutIsUnsynced(true); | |
857 D.PutSpecifics(encrypted_bookmark); | |
858 D.PutNonUniqueName("not encrypted"); | |
859 } | |
860 EXPECT_TRUE(SyncShareNudge()); | |
861 { | |
862 // Nothing should have commited due to bookmarks being encrypted and | |
863 // the cryptographer having pending keys. A would have been resolved | |
864 // as a simple conflict, but still be unsynced until the next sync cycle. | |
865 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
866 VERIFY_ENTRY(1, false, true, false, 0, 20, 20, ids_, &rtrans); | |
867 VERIFY_ENTRY(2, false, true, false, 0, 10, 10, ids_, &rtrans); | |
868 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans); | |
869 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans); | |
870 | |
871 // Resolve the pending keys. | |
872 GetCryptographer(&rtrans)->DecryptPendingKeys(other_params); | |
873 } | |
874 EXPECT_TRUE(SyncShareNudge()); | |
875 { | |
876 // All properly encrypted and non-conflicting items should commit. "A" was | |
877 // conflicting, but last sync cycle resolved it as simple conflict, so on | |
878 // this sync cycle it committed succesfullly. | |
879 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
880 // Committed successfully. | |
881 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans); | |
882 // Committed successfully. | |
883 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans); | |
884 // Was not properly encrypted. | |
885 VERIFY_ENTRY(3, false, true, false, 0, 10, 10, ids_, &rtrans); | |
886 // Was not properly encrypted. | |
887 VERIFY_ENTRY(4, false, true, false, 0, 10, 10, ids_, &rtrans); | |
888 } | |
889 { | |
890 // Fix the remaining items. | |
891 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
892 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3)); | |
893 ASSERT_TRUE(C.good()); | |
894 C.PutSpecifics(encrypted_bookmark); | |
895 C.PutNonUniqueName(kEncryptedString); | |
896 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4)); | |
897 ASSERT_TRUE(D.good()); | |
898 D.PutSpecifics(encrypted_bookmark); | |
899 D.PutNonUniqueName(kEncryptedString); | |
900 } | |
901 EXPECT_TRUE(SyncShareNudge()); | |
902 { | |
903 const StatusController& status_controller = session_->status_controller(); | |
904 // Expect success. | |
905 EXPECT_EQ(SYNCER_OK, status_controller.model_neutral_state().commit_result); | |
906 // None should be unsynced anymore. | |
907 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
908 VERIFY_ENTRY(1, false, false, false, 0, 21, 21, ids_, &rtrans); | |
909 VERIFY_ENTRY(2, false, false, false, 0, 11, 11, ids_, &rtrans); | |
910 VERIFY_ENTRY(3, false, false, false, 0, 11, 11, ids_, &rtrans); | |
911 VERIFY_ENTRY(4, false, false, false, 0, 11, 11, ids_, &rtrans); | |
912 } | |
913 } | |
914 | |
915 TEST_F(SyncerTest, GetUpdatesPartialThrottled) { | |
916 sync_pb::EntitySpecifics bookmark, pref; | |
917 bookmark.mutable_bookmark()->set_title("title"); | |
918 pref.mutable_preference()->set_name("name"); | |
919 AddDefaultFieldValue(BOOKMARKS, &bookmark); | |
920 AddDefaultFieldValue(PREFERENCES, &pref); | |
921 | |
922 // Normal sync, all the data types should get synced. | |
923 mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark, | |
924 foreign_cache_guid(), "-1"); | |
925 mock_server_->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark, | |
926 foreign_cache_guid(), "-2"); | |
927 mock_server_->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark, | |
928 foreign_cache_guid(), "-3"); | |
929 mock_server_->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref); | |
930 | |
931 EXPECT_TRUE(SyncShareNudge()); | |
932 { | |
933 // Initial state. Everything is normal. | |
934 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
935 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_, &rtrans); | |
936 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_, &rtrans); | |
937 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_, &rtrans); | |
938 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_, &rtrans); | |
939 } | |
940 | |
941 // Set BOOKMARKS throttled but PREFERENCES not, | |
942 // then BOOKMARKS should not get synced but PREFERENCES should. | |
943 ModelTypeSet throttled_types(BOOKMARKS); | |
944 mock_server_->set_partial_throttling(true); | |
945 mock_server_->SetThrottledTypes(throttled_types); | |
946 | |
947 mock_server_->AddUpdateSpecifics(1, 0, "E", 20, 20, true, 0, bookmark, | |
948 foreign_cache_guid(), "-1"); | |
949 mock_server_->AddUpdateSpecifics(2, 1, "F", 20, 20, false, 2, bookmark, | |
950 foreign_cache_guid(), "-2"); | |
951 mock_server_->AddUpdateSpecifics(3, 1, "G", 20, 20, false, 1, bookmark, | |
952 foreign_cache_guid(), "-3"); | |
953 mock_server_->AddUpdateSpecifics(4, 0, "H", 20, 20, false, 0, pref); | |
954 { | |
955 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
956 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
957 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); | |
958 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3)); | |
959 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4)); | |
960 A.PutIsUnsynced(true); | |
961 B.PutIsUnsynced(true); | |
962 C.PutIsUnsynced(true); | |
963 D.PutIsUnsynced(true); | |
964 } | |
965 EXPECT_TRUE(SyncShareNudge()); | |
966 { | |
967 // BOOKMARKS throttled. | |
968 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
969 VERIFY_ENTRY(1, false, true, false, 0, 10, 10, ids_, &rtrans); | |
970 VERIFY_ENTRY(2, false, true, false, 1, 10, 10, ids_, &rtrans); | |
971 VERIFY_ENTRY(3, false, true, false, 1, 10, 10, ids_, &rtrans); | |
972 VERIFY_ENTRY(4, false, false, false, 0, 21, 21, ids_, &rtrans); | |
973 } | |
974 | |
975 // Unthrottled BOOKMARKS, then BOOKMARKS should get synced now. | |
976 mock_server_->set_partial_throttling(false); | |
977 | |
978 mock_server_->AddUpdateSpecifics(1, 0, "E", 30, 30, true, 0, bookmark, | |
979 foreign_cache_guid(), "-1"); | |
980 mock_server_->AddUpdateSpecifics(2, 1, "F", 30, 30, false, 2, bookmark, | |
981 foreign_cache_guid(), "-2"); | |
982 mock_server_->AddUpdateSpecifics(3, 1, "G", 30, 30, false, 1, bookmark, | |
983 foreign_cache_guid(), "-3"); | |
984 mock_server_->AddUpdateSpecifics(4, 0, "H", 30, 30, false, 0, pref); | |
985 EXPECT_TRUE(SyncShareNudge()); | |
986 { | |
987 // BOOKMARKS unthrottled. | |
988 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
989 VERIFY_ENTRY(1, false, false, false, 0, 31, 31, ids_, &rtrans); | |
990 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_, &rtrans); | |
991 VERIFY_ENTRY(3, false, false, false, 1, 31, 31, ids_, &rtrans); | |
992 VERIFY_ENTRY(4, false, false, false, 0, 30, 30, ids_, &rtrans); | |
993 } | |
994 } | |
995 | |
996 // This test uses internal knowledge of the directory to test correctness of | |
997 // GetCommitIds. In almost every other test, the hierarchy is created from | |
998 // parent to child order, and so parents always have metahandles that are | |
999 // smaller than those of their children. This makes it very difficult to test | |
1000 // some GetCommitIds edge cases, since it uses metahandle ordering as | |
1001 // a starting point. | |
1002 TEST_F(SyncerTest, GetCommitIds_VerifyDeletionCommitOrder) { | |
1003 { | |
1004 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
1005 | |
1006 // Create four bookmarks folders at the root node. | |
1007 for (int i = 1; i < 5; ++i) { | |
1008 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), ""); | |
1009 entry.PutId(ids_.FromNumber(i)); | |
1010 entry.PutIsDir(true); | |
1011 entry.PutBaseVersion(5); | |
1012 entry.PutServerVersion(5); | |
1013 entry.PutServerParentId(trans.root_id()); | |
1014 entry.PutServerIsDir(true); | |
1015 entry.PutIsUnsynced(true); | |
1016 entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
1017 } | |
1018 | |
1019 // Now iterate in reverse order make a hierarchy of them. | |
1020 // While we're at it, also mark them as deleted. | |
1021 syncable::Id parent_id = trans.root_id(); | |
1022 for (int i = 4; i > 0; --i) { | |
1023 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(i)); | |
1024 entry.PutParentId(parent_id); | |
1025 entry.PutServerParentId(parent_id); | |
1026 entry.PutIsDel(true); | |
1027 parent_id = ids_.FromNumber(i); | |
1028 } | |
1029 } | |
1030 | |
1031 { | |
1032 // Run GetCommitIds, the function being tested. | |
1033 syncable::Directory::Metahandles result_handles; | |
1034 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
1035 GetCommitIdsForType(&trans, BOOKMARKS, 100, &result_handles); | |
1036 | |
1037 // Now verify the output. We expect four results in child to parent order. | |
1038 ASSERT_EQ(4U, result_handles.size()); | |
1039 | |
1040 Entry entry0(&trans, GET_BY_HANDLE, result_handles[0]); | |
1041 EXPECT_EQ(ids_.FromNumber(1), entry0.GetId()); | |
1042 | |
1043 Entry entry1(&trans, GET_BY_HANDLE, result_handles[1]); | |
1044 EXPECT_EQ(ids_.FromNumber(2), entry1.GetId()); | |
1045 | |
1046 Entry entry2(&trans, GET_BY_HANDLE, result_handles[2]); | |
1047 EXPECT_EQ(ids_.FromNumber(3), entry2.GetId()); | |
1048 | |
1049 Entry entry3(&trans, GET_BY_HANDLE, result_handles[3]); | |
1050 EXPECT_EQ(ids_.FromNumber(4), entry3.GetId()); | |
1051 } | |
1052 } | |
1053 | |
1054 // Verify that if there are more deleted items than the maximum number of | |
1055 // entries, child to parent order is still preserved. | |
1056 TEST_F(SyncerTest, GetCommitIds_VerifyDeletionCommitOrderMaxEntries) { | |
1057 { | |
1058 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
1059 | |
1060 // Create a bookmark tree with one root, two second level, and three third | |
1061 // level bookmarks, all folders. | |
1062 for (int i = 1; i <= 6; ++i) { | |
1063 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), ""); | |
1064 entry.PutId(ids_.FromNumber(i)); | |
1065 entry.PutIsDir(true); | |
1066 entry.PutBaseVersion(5); | |
1067 entry.PutServerVersion(5); | |
1068 entry.PutParentId(ids_.FromNumber(i/2)); | |
1069 entry.PutServerParentId(ids_.FromNumber(i/2)); | |
1070 entry.PutServerIsDir(true); | |
1071 entry.PutIsUnsynced(true); | |
1072 entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
1073 entry.PutIsDel(true); | |
1074 } | |
1075 } | |
1076 | |
1077 { | |
1078 // Run GetCommitIds with a limit of 2 entries to commit. | |
1079 syncable::Directory::Metahandles result_handles; | |
1080 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
1081 GetCommitIdsForType(&trans, BOOKMARKS, 2, &result_handles); | |
1082 | |
1083 // Now verify the output. We expect two results in child to parent order | |
1084 // (descending id order). | |
1085 ASSERT_EQ(2U, result_handles.size()); | |
1086 | |
1087 Entry entry0(&trans, GET_BY_HANDLE, result_handles[0]); | |
1088 EXPECT_EQ(ids_.FromNumber(6), entry0.GetId()); | |
1089 | |
1090 Entry entry1(&trans, GET_BY_HANDLE, result_handles[1]); | |
1091 EXPECT_EQ(ids_.FromNumber(5), entry1.GetId()); | |
1092 } | |
1093 } | |
1094 | |
1095 TEST_F(SyncerTest, EncryptionAwareConflicts) { | |
1096 KeyParams key_params = {"localhost", "dummy", "foobar"}; | |
1097 Cryptographer other_cryptographer(&encryptor_); | |
1098 other_cryptographer.AddKey(key_params); | |
1099 sync_pb::EntitySpecifics bookmark, encrypted_bookmark, modified_bookmark; | |
1100 bookmark.mutable_bookmark()->set_title("title"); | |
1101 other_cryptographer.Encrypt(bookmark, | |
1102 encrypted_bookmark.mutable_encrypted()); | |
1103 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark); | |
1104 modified_bookmark.mutable_bookmark()->set_title("title2"); | |
1105 other_cryptographer.Encrypt(modified_bookmark, | |
1106 modified_bookmark.mutable_encrypted()); | |
1107 sync_pb::EntitySpecifics pref, encrypted_pref, modified_pref; | |
1108 pref.mutable_preference()->set_name("name"); | |
1109 AddDefaultFieldValue(PREFERENCES, &encrypted_pref); | |
1110 other_cryptographer.Encrypt(pref, | |
1111 encrypted_pref.mutable_encrypted()); | |
1112 modified_pref.mutable_preference()->set_name("name2"); | |
1113 other_cryptographer.Encrypt(modified_pref, | |
1114 modified_pref.mutable_encrypted()); | |
1115 { | |
1116 // Mark bookmarks and preferences as encrypted and set the cryptographer to | |
1117 // have pending keys. | |
1118 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1119 sync_pb::EntitySpecifics specifics; | |
1120 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori(); | |
1121 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag()); | |
1122 dir_maker_.encryption_handler()->EnableEncryptEverything(); | |
1123 GetCryptographer(&wtrans)->SetPendingKeys(nigori->encryption_keybag()); | |
1124 EXPECT_TRUE(GetCryptographer(&wtrans)->has_pending_keys()); | |
1125 } | |
1126 | |
1127 // We need to remember the exact position of our local items, so we can | |
1128 // make updates that do not modify those positions. | |
1129 UniquePosition pos1; | |
1130 UniquePosition pos2; | |
1131 UniquePosition pos3; | |
1132 | |
1133 mock_server_->AddUpdateSpecifics(1, 0, "A", 10, 10, true, 0, bookmark, | |
1134 foreign_cache_guid(), "-1"); | |
1135 mock_server_->AddUpdateSpecifics(2, 1, "B", 10, 10, false, 2, bookmark, | |
1136 foreign_cache_guid(), "-2"); | |
1137 mock_server_->AddUpdateSpecifics(3, 1, "C", 10, 10, false, 1, bookmark, | |
1138 foreign_cache_guid(), "-3"); | |
1139 mock_server_->AddUpdateSpecifics(4, 0, "D", 10, 10, false, 0, pref); | |
1140 EXPECT_TRUE(SyncShareNudge()); | |
1141 { | |
1142 // Initial state. Everything is normal. | |
1143 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
1144 VERIFY_ENTRY(1, false, false, false, 0, 10, 10, ids_, &rtrans); | |
1145 VERIFY_ENTRY(2, false, false, false, 1, 10, 10, ids_, &rtrans); | |
1146 VERIFY_ENTRY(3, false, false, false, 1, 10, 10, ids_, &rtrans); | |
1147 VERIFY_ENTRY(4, false, false, false, 0, 10, 10, ids_, &rtrans); | |
1148 | |
1149 Entry entry1(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1)); | |
1150 ASSERT_TRUE(entry1.GetUniquePosition().Equals( | |
1151 entry1.GetServerUniquePosition())); | |
1152 pos1 = entry1.GetUniquePosition(); | |
1153 Entry entry2(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2)); | |
1154 pos2 = entry2.GetUniquePosition(); | |
1155 Entry entry3(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(3)); | |
1156 pos3 = entry3.GetUniquePosition(); | |
1157 } | |
1158 | |
1159 // Server side encryption will not be applied due to undecryptable data. | |
1160 // At this point, BASE_SERVER_SPECIFICS should be filled for all four items. | |
1161 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 20, 20, true, 0, | |
1162 encrypted_bookmark, | |
1163 foreign_cache_guid(), "-1"); | |
1164 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 20, 20, false, 2, | |
1165 encrypted_bookmark, | |
1166 foreign_cache_guid(), "-2"); | |
1167 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 20, 20, false, 1, | |
1168 encrypted_bookmark, | |
1169 foreign_cache_guid(), "-3"); | |
1170 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 20, 20, false, 0, | |
1171 encrypted_pref, | |
1172 foreign_cache_guid(), "-4"); | |
1173 EXPECT_TRUE(SyncShareNudge()); | |
1174 { | |
1175 // All should be unapplied due to being undecryptable and have a valid | |
1176 // BASE_SERVER_SPECIFICS. | |
1177 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
1178 VERIFY_ENTRY(1, true, false, true, 0, 10, 20, ids_, &rtrans); | |
1179 VERIFY_ENTRY(2, true, false, true, 1, 10, 20, ids_, &rtrans); | |
1180 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans); | |
1181 VERIFY_ENTRY(4, true, false, true, 0, 10, 20, ids_, &rtrans); | |
1182 } | |
1183 | |
1184 // Server side change that don't modify anything should not affect | |
1185 // BASE_SERVER_SPECIFICS (such as name changes and mtime changes). | |
1186 mock_server_->AddUpdateSpecifics(1, 0, kEncryptedString, 30, 30, true, 0, | |
1187 encrypted_bookmark, | |
1188 foreign_cache_guid(), "-1"); | |
1189 mock_server_->AddUpdateSpecifics(2, 1, kEncryptedString, 30, 30, false, 2, | |
1190 encrypted_bookmark, | |
1191 foreign_cache_guid(), "-2"); | |
1192 // Item 3 doesn't change. | |
1193 mock_server_->AddUpdateSpecifics(4, 0, kEncryptedString, 30, 30, false, 0, | |
1194 encrypted_pref, | |
1195 foreign_cache_guid(), "-4"); | |
1196 EXPECT_TRUE(SyncShareNudge()); | |
1197 { | |
1198 // Items 1, 2, and 4 should have newer server versions, 3 remains the same. | |
1199 // All should remain unapplied due to be undecryptable. | |
1200 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
1201 VERIFY_ENTRY(1, true, false, true, 0, 10, 30, ids_, &rtrans); | |
1202 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans); | |
1203 VERIFY_ENTRY(3, true, false, true, 1, 10, 20, ids_, &rtrans); | |
1204 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans); | |
1205 } | |
1206 | |
1207 // Positional changes, parent changes, and specifics changes should reset | |
1208 // BASE_SERVER_SPECIFICS. | |
1209 // Became unencrypted. | |
1210 mock_server_->AddUpdateSpecifics(1, 0, "A", 40, 40, true, 0, bookmark, | |
1211 foreign_cache_guid(), "-1"); | |
1212 // Reordered to after item 2. | |
1213 mock_server_->AddUpdateSpecifics(3, 1, kEncryptedString, 30, 30, false, 3, | |
1214 encrypted_bookmark, | |
1215 foreign_cache_guid(), "-3"); | |
1216 EXPECT_TRUE(SyncShareNudge()); | |
1217 { | |
1218 // Items 2 and 4 should be the only ones with BASE_SERVER_SPECIFICS set. | |
1219 // Items 1 is now unencrypted, so should have applied normally. | |
1220 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
1221 VERIFY_ENTRY(1, false, false, false, 0, 40, 40, ids_, &rtrans); | |
1222 VERIFY_ENTRY(2, true, false, true, 1, 10, 30, ids_, &rtrans); | |
1223 VERIFY_ENTRY(3, true, false, false, 1, 10, 30, ids_, &rtrans); | |
1224 VERIFY_ENTRY(4, true, false, true, 0, 10, 30, ids_, &rtrans); | |
1225 } | |
1226 | |
1227 // Make local changes, which should remain unsynced for items 2, 3, 4. | |
1228 { | |
1229 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1230 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
1231 ASSERT_TRUE(A.good()); | |
1232 A.PutSpecifics(modified_bookmark); | |
1233 A.PutNonUniqueName(kEncryptedString); | |
1234 A.PutIsUnsynced(true); | |
1235 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); | |
1236 ASSERT_TRUE(B.good()); | |
1237 B.PutSpecifics(modified_bookmark); | |
1238 B.PutNonUniqueName(kEncryptedString); | |
1239 B.PutIsUnsynced(true); | |
1240 MutableEntry C(&wtrans, GET_BY_ID, ids_.FromNumber(3)); | |
1241 ASSERT_TRUE(C.good()); | |
1242 C.PutSpecifics(modified_bookmark); | |
1243 C.PutNonUniqueName(kEncryptedString); | |
1244 C.PutIsUnsynced(true); | |
1245 MutableEntry D(&wtrans, GET_BY_ID, ids_.FromNumber(4)); | |
1246 ASSERT_TRUE(D.good()); | |
1247 D.PutSpecifics(modified_pref); | |
1248 D.PutNonUniqueName(kEncryptedString); | |
1249 D.PutIsUnsynced(true); | |
1250 } | |
1251 EXPECT_TRUE(SyncShareNudge()); | |
1252 { | |
1253 // Item 1 remains unsynced due to there being pending keys. | |
1254 // Items 2, 3, 4 should remain unsynced since they were not up to date. | |
1255 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
1256 VERIFY_ENTRY(1, false, true, false, 0, 40, 40, ids_, &rtrans); | |
1257 VERIFY_ENTRY(2, true, true, true, 1, 10, 30, ids_, &rtrans); | |
1258 VERIFY_ENTRY(3, true, true, false, 1, 10, 30, ids_, &rtrans); | |
1259 VERIFY_ENTRY(4, true, true, true, 0, 10, 30, ids_, &rtrans); | |
1260 } | |
1261 | |
1262 { | |
1263 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
1264 // Resolve the pending keys. | |
1265 GetCryptographer(&rtrans)->DecryptPendingKeys(key_params); | |
1266 } | |
1267 // First cycle resolves conflicts, second cycle commits changes. | |
1268 EXPECT_TRUE(SyncShareNudge()); | |
1269 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_server_overwrites); | |
1270 EXPECT_EQ(1, GetUpdateCounters(PREFERENCES).num_server_overwrites); | |
1271 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_local_overwrites); | |
1272 | |
1273 // We successfully commited item(s). | |
1274 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS).num_commits_attempted); | |
1275 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS).num_commits_success); | |
1276 EXPECT_EQ(1, GetCommitCounters(PREFERENCES).num_commits_attempted); | |
1277 EXPECT_EQ(1, GetCommitCounters(PREFERENCES).num_commits_success); | |
1278 | |
1279 EXPECT_TRUE(SyncShareNudge()); | |
1280 | |
1281 // Everything should be resolved now. The local changes should have | |
1282 // overwritten the server changes for 2 and 4, while the server changes | |
1283 // overwrote the local for entry 3. | |
1284 // | |
1285 // Expect there will be no new overwrites. | |
1286 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_server_overwrites); | |
1287 EXPECT_EQ(1, GetUpdateCounters(BOOKMARKS).num_local_overwrites); | |
1288 | |
1289 EXPECT_EQ(2, GetCommitCounters(BOOKMARKS).num_commits_success); | |
1290 EXPECT_EQ(1, GetCommitCounters(PREFERENCES).num_commits_success); | |
1291 | |
1292 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
1293 VERIFY_ENTRY(1, false, false, false, 0, 41, 41, ids_, &rtrans); | |
1294 VERIFY_ENTRY(2, false, false, false, 1, 31, 31, ids_, &rtrans); | |
1295 VERIFY_ENTRY(3, false, false, false, 1, 30, 30, ids_, &rtrans); | |
1296 VERIFY_ENTRY(4, false, false, false, 0, 31, 31, ids_, &rtrans); | |
1297 } | |
1298 | |
1299 #undef VERIFY_ENTRY | |
1300 | |
1301 TEST_F(SyncerTest, TestGetUnsyncedAndSimpleCommit) { | |
1302 { | |
1303 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1304 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete"); | |
1305 ASSERT_TRUE(parent.good()); | |
1306 parent.PutIsUnsynced(true); | |
1307 parent.PutIsDir(true); | |
1308 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1309 parent.PutBaseVersion(1); | |
1310 parent.PutId(parent_id_); | |
1311 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete"); | |
1312 ASSERT_TRUE(child.good()); | |
1313 child.PutId(child_id_); | |
1314 child.PutBaseVersion(1); | |
1315 WriteTestDataToEntry(&wtrans, &child); | |
1316 } | |
1317 | |
1318 EXPECT_TRUE(SyncShareNudge()); | |
1319 ASSERT_EQ(2u, mock_server_->committed_ids().size()); | |
1320 // If this test starts failing, be aware other sort orders could be valid. | |
1321 EXPECT_EQ(parent_id_, mock_server_->committed_ids()[0]); | |
1322 EXPECT_EQ(child_id_, mock_server_->committed_ids()[1]); | |
1323 { | |
1324 syncable::ReadTransaction rt(FROM_HERE, directory()); | |
1325 Entry entry(&rt, syncable::GET_BY_ID, child_id_); | |
1326 ASSERT_TRUE(entry.good()); | |
1327 VerifyTestDataInEntry(&rt, &entry); | |
1328 } | |
1329 } | |
1330 | |
1331 TEST_F(SyncerTest, TestPurgeWhileUnsynced) { | |
1332 // Similar to above, but throw a purge operation into the mix. Bug 49278. | |
1333 syncable::Id pref_node_id = TestIdFactory::MakeServer("Tim"); | |
1334 { | |
1335 directory()->SetDownloadProgress(BOOKMARKS, | |
1336 syncable::BuildProgress(BOOKMARKS)); | |
1337 directory()->SetDownloadProgress(PREFERENCES, | |
1338 syncable::BuildProgress(PREFERENCES)); | |
1339 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1340 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete"); | |
1341 ASSERT_TRUE(parent.good()); | |
1342 parent.PutIsUnsynced(true); | |
1343 parent.PutIsDir(true); | |
1344 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1345 parent.PutBaseVersion(1); | |
1346 parent.PutId(parent_id_); | |
1347 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete"); | |
1348 ASSERT_TRUE(child.good()); | |
1349 child.PutId(child_id_); | |
1350 child.PutBaseVersion(1); | |
1351 WriteTestDataToEntry(&wtrans, &child); | |
1352 | |
1353 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Tim"); | |
1354 ASSERT_TRUE(parent2.good()); | |
1355 parent2.PutIsUnsynced(true); | |
1356 parent2.PutIsDir(true); | |
1357 parent2.PutSpecifics(DefaultPreferencesSpecifics()); | |
1358 parent2.PutBaseVersion(1); | |
1359 parent2.PutId(pref_node_id); | |
1360 } | |
1361 | |
1362 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES), | |
1363 ModelTypeSet(), | |
1364 ModelTypeSet()); | |
1365 | |
1366 EXPECT_TRUE(SyncShareNudge()); | |
1367 ASSERT_EQ(2U, mock_server_->committed_ids().size()); | |
1368 // If this test starts failing, be aware other sort orders could be valid. | |
1369 EXPECT_EQ(parent_id_, mock_server_->committed_ids()[0]); | |
1370 EXPECT_EQ(child_id_, mock_server_->committed_ids()[1]); | |
1371 { | |
1372 syncable::ReadTransaction rt(FROM_HERE, directory()); | |
1373 Entry entry(&rt, syncable::GET_BY_ID, child_id_); | |
1374 ASSERT_TRUE(entry.good()); | |
1375 VerifyTestDataInEntry(&rt, &entry); | |
1376 } | |
1377 directory()->SaveChanges(); | |
1378 { | |
1379 syncable::ReadTransaction rt(FROM_HERE, directory()); | |
1380 Entry entry(&rt, syncable::GET_BY_ID, pref_node_id); | |
1381 ASSERT_FALSE(entry.good()); | |
1382 } | |
1383 } | |
1384 | |
1385 TEST_F(SyncerTest, TestPurgeWhileUnapplied) { | |
1386 // Similar to above, but for unapplied items. Bug 49278. | |
1387 { | |
1388 directory()->SetDownloadProgress(BOOKMARKS, | |
1389 syncable::BuildProgress(BOOKMARKS)); | |
1390 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1391 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete"); | |
1392 ASSERT_TRUE(parent.good()); | |
1393 parent.PutIsUnappliedUpdate(true); | |
1394 parent.PutIsDir(true); | |
1395 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1396 parent.PutBaseVersion(1); | |
1397 parent.PutId(parent_id_); | |
1398 } | |
1399 | |
1400 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(BOOKMARKS), | |
1401 ModelTypeSet(), | |
1402 ModelTypeSet()); | |
1403 | |
1404 EXPECT_TRUE(SyncShareNudge()); | |
1405 directory()->SaveChanges(); | |
1406 { | |
1407 syncable::ReadTransaction rt(FROM_HERE, directory()); | |
1408 Entry entry(&rt, syncable::GET_BY_ID, parent_id_); | |
1409 ASSERT_FALSE(entry.good()); | |
1410 } | |
1411 } | |
1412 | |
1413 TEST_F(SyncerTest, TestPurgeWithJournal) { | |
1414 { | |
1415 directory()->SetDownloadProgress(BOOKMARKS, | |
1416 syncable::BuildProgress(BOOKMARKS)); | |
1417 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1418 MutableEntry parent(&wtrans, syncable::CREATE, BOOKMARKS, wtrans.root_id(), | |
1419 "Pete"); | |
1420 ASSERT_TRUE(parent.good()); | |
1421 parent.PutIsDir(true); | |
1422 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1423 parent.PutBaseVersion(1); | |
1424 parent.PutId(parent_id_); | |
1425 MutableEntry child(&wtrans, syncable::CREATE, BOOKMARKS, parent_id_, | |
1426 "Pete"); | |
1427 ASSERT_TRUE(child.good()); | |
1428 child.PutId(child_id_); | |
1429 child.PutBaseVersion(1); | |
1430 WriteTestDataToEntry(&wtrans, &child); | |
1431 | |
1432 MutableEntry parent2(&wtrans, syncable::CREATE, PREFERENCES, | |
1433 wtrans.root_id(), "Tim"); | |
1434 ASSERT_TRUE(parent2.good()); | |
1435 parent2.PutIsDir(true); | |
1436 parent2.PutSpecifics(DefaultPreferencesSpecifics()); | |
1437 parent2.PutBaseVersion(1); | |
1438 parent2.PutId(TestIdFactory::MakeServer("Tim")); | |
1439 } | |
1440 | |
1441 directory()->PurgeEntriesWithTypeIn(ModelTypeSet(PREFERENCES, BOOKMARKS), | |
1442 ModelTypeSet(BOOKMARKS), | |
1443 ModelTypeSet()); | |
1444 { | |
1445 // Verify bookmark nodes are saved in delete journal but not preference | |
1446 // node. | |
1447 syncable::ReadTransaction rt(FROM_HERE, directory()); | |
1448 syncable::DeleteJournal* delete_journal = directory()->delete_journal(); | |
1449 EXPECT_EQ(2u, delete_journal->GetDeleteJournalSize(&rt)); | |
1450 syncable::EntryKernelSet journal_entries; | |
1451 directory()->delete_journal()->GetDeleteJournals(&rt, BOOKMARKS, | |
1452 &journal_entries); | |
1453 EXPECT_EQ(parent_id_, (*journal_entries.begin())->ref(syncable::ID)); | |
1454 EXPECT_EQ(child_id_, (*journal_entries.rbegin())->ref(syncable::ID)); | |
1455 } | |
1456 } | |
1457 | |
1458 TEST_F(SyncerTest, ResetVersions) { | |
1459 // Download some pref items. | |
1460 mock_server_->AddUpdatePref("id1", "", "tag1", 20, 20); | |
1461 mock_server_->AddUpdatePref("id2", "", "tag2", 30, 30); | |
1462 mock_server_->AddUpdatePref("id3", "", "tag3", 40, 40); | |
1463 EXPECT_TRUE(SyncShareNudge()); | |
1464 | |
1465 { | |
1466 // Modify one of the preferences locally, mark another one as unapplied, | |
1467 // and create another unsynced preference. | |
1468 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1469 MutableEntry entry(&wtrans, GET_BY_CLIENT_TAG, "tag1"); | |
1470 entry.PutIsUnsynced(true); | |
1471 | |
1472 MutableEntry entry2(&wtrans, GET_BY_CLIENT_TAG, "tag2"); | |
1473 entry2.PutIsUnappliedUpdate(true); | |
1474 | |
1475 MutableEntry entry4(&wtrans, CREATE, PREFERENCES, "name"); | |
1476 entry4.PutUniqueClientTag("tag4"); | |
1477 entry4.PutIsUnsynced(true); | |
1478 } | |
1479 | |
1480 { | |
1481 // Reset the versions. | |
1482 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1483 ASSERT_TRUE(directory()->ResetVersionsForType(&wtrans, PREFERENCES)); | |
1484 } | |
1485 | |
1486 { | |
1487 // Verify the synced items are all with version 1 now, with | |
1488 // unsynced/unapplied state preserved. | |
1489 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
1490 Entry entry(&trans, GET_BY_CLIENT_TAG, "tag1"); | |
1491 EXPECT_EQ(1, entry.GetBaseVersion()); | |
1492 EXPECT_EQ(1, entry.GetServerVersion()); | |
1493 EXPECT_TRUE(entry.GetIsUnsynced()); | |
1494 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
1495 Entry entry2(&trans, GET_BY_CLIENT_TAG, "tag2"); | |
1496 EXPECT_EQ(1, entry2.GetBaseVersion()); | |
1497 EXPECT_EQ(1, entry2.GetServerVersion()); | |
1498 EXPECT_FALSE(entry2.GetIsUnsynced()); | |
1499 EXPECT_TRUE(entry2.GetIsUnappliedUpdate()); | |
1500 Entry entry3(&trans, GET_BY_CLIENT_TAG, "tag3"); | |
1501 EXPECT_EQ(1, entry3.GetBaseVersion()); | |
1502 EXPECT_EQ(1, entry3.GetServerVersion()); | |
1503 EXPECT_FALSE(entry3.GetIsUnsynced()); | |
1504 EXPECT_FALSE(entry3.GetIsUnappliedUpdate()); | |
1505 | |
1506 // Entry 4 (the locally created one) should remain the same. | |
1507 Entry entry4(&trans, GET_BY_CLIENT_TAG, "tag4"); | |
1508 EXPECT_EQ(-1, entry4.GetBaseVersion()); | |
1509 EXPECT_EQ(0, entry4.GetServerVersion()); | |
1510 EXPECT_TRUE(entry4.GetIsUnsynced()); | |
1511 EXPECT_FALSE(entry4.GetIsUnappliedUpdate()); | |
1512 } | |
1513 } | |
1514 | |
1515 TEST_F(SyncerTest, TestCommitListOrderingTwoItemsTall) { | |
1516 CommitOrderingTest items[] = { | |
1517 {1, ids_.FromNumber(-1001), ids_.FromNumber(-1000)}, | |
1518 {0, ids_.FromNumber(-1000), ids_.FromNumber(0)}, | |
1519 CommitOrderingTest::MakeLastCommitItem(), | |
1520 }; | |
1521 RunCommitOrderingTest(items); | |
1522 } | |
1523 | |
1524 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTall) { | |
1525 CommitOrderingTest items[] = { | |
1526 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)}, | |
1527 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)}, | |
1528 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)}, | |
1529 CommitOrderingTest::MakeLastCommitItem(), | |
1530 }; | |
1531 RunCommitOrderingTest(items); | |
1532 } | |
1533 | |
1534 TEST_F(SyncerTest, TestCommitListOrderingFourItemsTall) { | |
1535 CommitOrderingTest items[] = { | |
1536 {3, ids_.FromNumber(-2003), ids_.FromNumber(-2002)}, | |
1537 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)}, | |
1538 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)}, | |
1539 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)}, | |
1540 CommitOrderingTest::MakeLastCommitItem(), | |
1541 }; | |
1542 RunCommitOrderingTest(items); | |
1543 } | |
1544 | |
1545 TEST_F(SyncerTest, TestCommitListOrderingThreeItemsTallLimitedSize) { | |
1546 context_->set_max_commit_batch_size(2); | |
1547 CommitOrderingTest items[] = { | |
1548 {1, ids_.FromNumber(-2001), ids_.FromNumber(-2000)}, | |
1549 {0, ids_.FromNumber(-2000), ids_.FromNumber(0)}, | |
1550 {2, ids_.FromNumber(-2002), ids_.FromNumber(-2001)}, | |
1551 CommitOrderingTest::MakeLastCommitItem(), | |
1552 }; | |
1553 RunCommitOrderingTest(items); | |
1554 } | |
1555 | |
1556 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItem) { | |
1557 CommitOrderingTest items[] = { | |
1558 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}}, | |
1559 CommitOrderingTest::MakeLastCommitItem(), | |
1560 }; | |
1561 RunCommitOrderingTest(items); | |
1562 } | |
1563 | |
1564 TEST_F(SyncerTest, TestCommitListOrderingSingleUncommittedDeletedItem) { | |
1565 CommitOrderingTest items[] = { | |
1566 {-1, ids_.FromNumber(-1000), ids_.FromNumber(0), {DELETED}}, | |
1567 CommitOrderingTest::MakeLastCommitItem(), | |
1568 }; | |
1569 RunCommitOrderingTest(items); | |
1570 } | |
1571 | |
1572 TEST_F(SyncerTest, TestCommitListOrderingSingleDeletedItemWithUnroll) { | |
1573 CommitOrderingTest items[] = { | |
1574 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}}, | |
1575 CommitOrderingTest::MakeLastCommitItem(), | |
1576 }; | |
1577 RunCommitOrderingTest(items); | |
1578 } | |
1579 | |
1580 TEST_F(SyncerTest, | |
1581 TestCommitListOrderingSingleLongDeletedItemWithUnroll) { | |
1582 CommitOrderingTest items[] = { | |
1583 {0, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}}, | |
1584 CommitOrderingTest::MakeLastCommitItem(), | |
1585 }; | |
1586 RunCommitOrderingTest(items); | |
1587 } | |
1588 | |
1589 TEST_F(SyncerTest, TestCommitListOrderingTwoLongDeletedItemWithUnroll) { | |
1590 CommitOrderingTest items[] = { | |
1591 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}}, | |
1592 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}}, | |
1593 CommitOrderingTest::MakeLastCommitItem(), | |
1594 }; | |
1595 RunCommitOrderingTest(items); | |
1596 } | |
1597 | |
1598 TEST_F(SyncerTest, TestCommitListOrdering3LongDeletedItemsWithSizeLimit) { | |
1599 context_->set_max_commit_batch_size(2); | |
1600 CommitOrderingTest items[] = { | |
1601 {2, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}}, | |
1602 {1, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME}}, | |
1603 {0, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}}, | |
1604 CommitOrderingTest::MakeLastCommitItem(), | |
1605 }; | |
1606 RunCommitOrderingTest(items); | |
1607 } | |
1608 | |
1609 TEST_F(SyncerTest, TestCommitListOrderingTwoDeletedItemsWithUnroll) { | |
1610 CommitOrderingTest items[] = { | |
1611 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED}}, | |
1612 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED}}, | |
1613 CommitOrderingTest::MakeLastCommitItem(), | |
1614 }; | |
1615 RunCommitOrderingTest(items); | |
1616 } | |
1617 | |
1618 TEST_F(SyncerTest, TestCommitListOrderingComplexDeletionScenario) { | |
1619 CommitOrderingTest items[] = { | |
1620 {2, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}}, | |
1621 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}}, | |
1622 {1, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}}, | |
1623 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}}, | |
1624 {0, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}}, | |
1625 CommitOrderingTest::MakeLastCommitItem(), | |
1626 }; | |
1627 RunCommitOrderingTest(items); | |
1628 } | |
1629 | |
1630 TEST_F(SyncerTest, | |
1631 TestCommitListOrderingComplexDeletionScenarioWith2RecentDeletes) { | |
1632 CommitOrderingTest items[] = { | |
1633 {3, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}}, | |
1634 {-1, ids_.FromNumber(1001), ids_.FromNumber(0), {SYNCED}}, | |
1635 {2, ids_.FromNumber(1002), ids_.FromNumber(1001), {DELETED, OLD_MTIME}}, | |
1636 {-1, ids_.FromNumber(1003), ids_.FromNumber(1001), {SYNCED}}, | |
1637 {1, ids_.FromNumber(1004), ids_.FromNumber(1003), {DELETED}}, | |
1638 {0, ids_.FromNumber(1005), ids_.FromNumber(1003), {DELETED}}, | |
1639 CommitOrderingTest::MakeLastCommitItem(), | |
1640 }; | |
1641 RunCommitOrderingTest(items); | |
1642 } | |
1643 | |
1644 TEST_F(SyncerTest, TestCommitListOrderingDeleteMovedItems) { | |
1645 CommitOrderingTest items[] = { | |
1646 {1, ids_.FromNumber(1000), ids_.FromNumber(0), {DELETED, OLD_MTIME}}, | |
1647 {0, ids_.FromNumber(1001), ids_.FromNumber(1000), {DELETED, OLD_MTIME, | |
1648 MOVED_FROM_ROOT}}, | |
1649 CommitOrderingTest::MakeLastCommitItem(), | |
1650 }; | |
1651 RunCommitOrderingTest(items); | |
1652 } | |
1653 | |
1654 TEST_F(SyncerTest, TestCommitListOrderingWithNesting) { | |
1655 const base::Time& now_minus_2h = | |
1656 base::Time::Now() - base::TimeDelta::FromHours(2); | |
1657 { | |
1658 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1659 { | |
1660 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bob"); | |
1661 ASSERT_TRUE(parent.good()); | |
1662 parent.PutIsUnsynced(true); | |
1663 parent.PutIsDir(true); | |
1664 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1665 parent.PutId(ids_.FromNumber(100)); | |
1666 parent.PutBaseVersion(1); | |
1667 MutableEntry child( | |
1668 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(100), "Bob"); | |
1669 ASSERT_TRUE(child.good()); | |
1670 child.PutIsUnsynced(true); | |
1671 child.PutIsDir(true); | |
1672 child.PutSpecifics(DefaultBookmarkSpecifics()); | |
1673 child.PutId(ids_.FromNumber(101)); | |
1674 child.PutBaseVersion(1); | |
1675 MutableEntry grandchild( | |
1676 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(101), "Bob"); | |
1677 ASSERT_TRUE(grandchild.good()); | |
1678 grandchild.PutId(ids_.FromNumber(102)); | |
1679 grandchild.PutIsUnsynced(true); | |
1680 grandchild.PutSpecifics(DefaultBookmarkSpecifics()); | |
1681 grandchild.PutBaseVersion(1); | |
1682 } | |
1683 { | |
1684 // Create three deleted items which deletions we expect to be sent to the | |
1685 // server. | |
1686 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Pete"); | |
1687 ASSERT_TRUE(parent.good()); | |
1688 parent.PutId(ids_.FromNumber(103)); | |
1689 parent.PutIsUnsynced(true); | |
1690 parent.PutIsDir(true); | |
1691 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1692 parent.PutIsDel(true); | |
1693 parent.PutBaseVersion(1); | |
1694 parent.PutMtime(now_minus_2h); | |
1695 MutableEntry child( | |
1696 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(103), "Pete"); | |
1697 ASSERT_TRUE(child.good()); | |
1698 child.PutId(ids_.FromNumber(104)); | |
1699 child.PutIsUnsynced(true); | |
1700 child.PutIsDir(true); | |
1701 child.PutSpecifics(DefaultBookmarkSpecifics()); | |
1702 child.PutIsDel(true); | |
1703 child.PutBaseVersion(1); | |
1704 child.PutMtime(now_minus_2h); | |
1705 MutableEntry grandchild( | |
1706 &wtrans, CREATE, BOOKMARKS, ids_.FromNumber(104), "Pete"); | |
1707 ASSERT_TRUE(grandchild.good()); | |
1708 grandchild.PutId(ids_.FromNumber(105)); | |
1709 grandchild.PutIsUnsynced(true); | |
1710 grandchild.PutIsDel(true); | |
1711 grandchild.PutIsDir(false); | |
1712 grandchild.PutSpecifics(DefaultBookmarkSpecifics()); | |
1713 grandchild.PutBaseVersion(1); | |
1714 grandchild.PutMtime(now_minus_2h); | |
1715 } | |
1716 } | |
1717 | |
1718 EXPECT_TRUE(SyncShareNudge()); | |
1719 ASSERT_EQ(6u, mock_server_->committed_ids().size()); | |
1720 // This test will NOT unroll deletes because SERVER_PARENT_ID is not set. | |
1721 // It will treat these like moves. | |
1722 vector<syncable::Id> commit_ids(mock_server_->committed_ids()); | |
1723 EXPECT_EQ(ids_.FromNumber(100), commit_ids[0]); | |
1724 EXPECT_EQ(ids_.FromNumber(101), commit_ids[1]); | |
1725 EXPECT_EQ(ids_.FromNumber(102), commit_ids[2]); | |
1726 // We don't guarantee the delete orders in this test, only that they occur | |
1727 // at the end. | |
1728 std::sort(commit_ids.begin() + 3, commit_ids.end()); | |
1729 EXPECT_EQ(ids_.FromNumber(103), commit_ids[3]); | |
1730 EXPECT_EQ(ids_.FromNumber(104), commit_ids[4]); | |
1731 EXPECT_EQ(ids_.FromNumber(105), commit_ids[5]); | |
1732 } | |
1733 | |
1734 TEST_F(SyncerTest, TestCommitListOrderingWithNewItems) { | |
1735 syncable::Id parent1_id = ids_.MakeServer("p1"); | |
1736 syncable::Id parent2_id = ids_.MakeServer("p2"); | |
1737 | |
1738 { | |
1739 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1740 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "1"); | |
1741 ASSERT_TRUE(parent.good()); | |
1742 parent.PutIsUnsynced(true); | |
1743 parent.PutIsDir(true); | |
1744 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1745 parent.PutId(parent1_id); | |
1746 MutableEntry child(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "2"); | |
1747 ASSERT_TRUE(child.good()); | |
1748 child.PutIsUnsynced(true); | |
1749 child.PutIsDir(true); | |
1750 child.PutSpecifics(DefaultBookmarkSpecifics()); | |
1751 child.PutId(parent2_id); | |
1752 parent.PutBaseVersion(1); | |
1753 child.PutBaseVersion(1); | |
1754 } | |
1755 { | |
1756 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1757 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent1_id, "A"); | |
1758 ASSERT_TRUE(parent.good()); | |
1759 parent.PutIsUnsynced(true); | |
1760 parent.PutIsDir(true); | |
1761 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1762 parent.PutId(ids_.FromNumber(102)); | |
1763 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent1_id, "B"); | |
1764 ASSERT_TRUE(child.good()); | |
1765 child.PutIsUnsynced(true); | |
1766 child.PutIsDir(true); | |
1767 child.PutSpecifics(DefaultBookmarkSpecifics()); | |
1768 child.PutId(ids_.FromNumber(-103)); | |
1769 parent.PutBaseVersion(1); | |
1770 } | |
1771 { | |
1772 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1773 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, parent2_id, "A"); | |
1774 ASSERT_TRUE(parent.good()); | |
1775 parent.PutIsUnsynced(true); | |
1776 parent.PutIsDir(true); | |
1777 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1778 parent.PutId(ids_.FromNumber(-104)); | |
1779 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent2_id, "B"); | |
1780 ASSERT_TRUE(child.good()); | |
1781 child.PutIsUnsynced(true); | |
1782 child.PutIsDir(true); | |
1783 child.PutSpecifics(DefaultBookmarkSpecifics()); | |
1784 child.PutId(ids_.FromNumber(105)); | |
1785 child.PutBaseVersion(1); | |
1786 } | |
1787 | |
1788 EXPECT_TRUE(SyncShareNudge()); | |
1789 ASSERT_EQ(6u, mock_server_->committed_ids().size()); | |
1790 | |
1791 // This strange iteration and std::count() usage is to allow the order to | |
1792 // vary. All we really care about is that parent1_id and parent2_id are the | |
1793 // first two IDs, and that the children make up the next four. Other than | |
1794 // that, ordering doesn't matter. | |
1795 | |
1796 vector<syncable::Id>::const_iterator i = | |
1797 mock_server_->committed_ids().begin(); | |
1798 vector<syncable::Id>::const_iterator parents_begin = i; | |
1799 i++; | |
1800 i++; | |
1801 vector<syncable::Id>::const_iterator parents_end = i; | |
1802 vector<syncable::Id>::const_iterator children_begin = i; | |
1803 vector<syncable::Id>::const_iterator children_end = | |
1804 mock_server_->committed_ids().end(); | |
1805 | |
1806 EXPECT_EQ(1, count(parents_begin, parents_end, parent1_id)); | |
1807 EXPECT_EQ(1, count(parents_begin, parents_end, parent2_id)); | |
1808 | |
1809 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-103))); | |
1810 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(102))); | |
1811 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(105))); | |
1812 EXPECT_EQ(1, count(children_begin, children_end, ids_.FromNumber(-104))); | |
1813 } | |
1814 | |
1815 TEST_F(SyncerTest, TestCommitListOrderingCounterexample) { | |
1816 syncable::Id child2_id = ids_.NewServerId(); | |
1817 | |
1818 { | |
1819 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1820 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "P"); | |
1821 ASSERT_TRUE(parent.good()); | |
1822 parent.PutIsUnsynced(true); | |
1823 parent.PutIsDir(true); | |
1824 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1825 parent.PutId(parent_id_); | |
1826 MutableEntry child1(&wtrans, CREATE, BOOKMARKS, parent_id_, "1"); | |
1827 ASSERT_TRUE(child1.good()); | |
1828 child1.PutIsUnsynced(true); | |
1829 child1.PutId(child_id_); | |
1830 child1.PutSpecifics(DefaultBookmarkSpecifics()); | |
1831 MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent_id_, "2"); | |
1832 ASSERT_TRUE(child2.good()); | |
1833 child2.PutIsUnsynced(true); | |
1834 child2.PutSpecifics(DefaultBookmarkSpecifics()); | |
1835 child2.PutId(child2_id); | |
1836 | |
1837 parent.PutBaseVersion(1); | |
1838 child1.PutBaseVersion(1); | |
1839 child2.PutBaseVersion(1); | |
1840 } | |
1841 | |
1842 EXPECT_TRUE(SyncShareNudge()); | |
1843 ASSERT_EQ(3u, mock_server_->committed_ids().size()); | |
1844 EXPECT_EQ(parent_id_, mock_server_->committed_ids()[0]); | |
1845 // There are two possible valid orderings. | |
1846 if (child2_id == mock_server_->committed_ids()[1]) { | |
1847 EXPECT_EQ(child2_id, mock_server_->committed_ids()[1]); | |
1848 EXPECT_EQ(child_id_, mock_server_->committed_ids()[2]); | |
1849 } else { | |
1850 EXPECT_EQ(child_id_, mock_server_->committed_ids()[1]); | |
1851 EXPECT_EQ(child2_id, mock_server_->committed_ids()[2]); | |
1852 } | |
1853 } | |
1854 | |
1855 TEST_F(SyncerTest, TestCommitListOrderingAndNewParent) { | |
1856 string parent1_name = "1"; | |
1857 string parent2_name = "A"; | |
1858 string child_name = "B"; | |
1859 | |
1860 { | |
1861 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1862 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), | |
1863 parent1_name); | |
1864 ASSERT_TRUE(parent.good()); | |
1865 parent.PutIsUnsynced(true); | |
1866 parent.PutIsDir(true); | |
1867 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1868 parent.PutId(parent_id_); | |
1869 parent.PutBaseVersion(1); | |
1870 } | |
1871 | |
1872 syncable::Id parent2_id = ids_.NewLocalId(); | |
1873 syncable::Id child_id = ids_.NewServerId(); | |
1874 { | |
1875 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1876 MutableEntry parent2( | |
1877 &wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name); | |
1878 ASSERT_TRUE(parent2.good()); | |
1879 parent2.PutIsUnsynced(true); | |
1880 parent2.PutIsDir(true); | |
1881 parent2.PutSpecifics(DefaultBookmarkSpecifics()); | |
1882 parent2.PutId(parent2_id); | |
1883 | |
1884 MutableEntry child( | |
1885 &wtrans, CREATE, BOOKMARKS, parent2_id, child_name); | |
1886 ASSERT_TRUE(child.good()); | |
1887 child.PutIsUnsynced(true); | |
1888 child.PutIsDir(true); | |
1889 child.PutSpecifics(DefaultBookmarkSpecifics()); | |
1890 child.PutId(child_id); | |
1891 child.PutBaseVersion(1); | |
1892 } | |
1893 | |
1894 EXPECT_TRUE(SyncShareNudge()); | |
1895 ASSERT_EQ(3u, mock_server_->committed_ids().size()); | |
1896 // If this test starts failing, be aware other sort orders could be valid. | |
1897 EXPECT_EQ(parent_id_, mock_server_->committed_ids()[0]); | |
1898 EXPECT_EQ(parent2_id, mock_server_->committed_ids()[1]); | |
1899 EXPECT_EQ(child_id, mock_server_->committed_ids()[2]); | |
1900 { | |
1901 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
1902 // Check that things committed correctly. | |
1903 Entry entry_1(&rtrans, syncable::GET_BY_ID, parent_id_); | |
1904 EXPECT_EQ(parent1_name, entry_1.GetNonUniqueName()); | |
1905 // Check that parent2 is a subfolder of parent1. | |
1906 EXPECT_EQ(1, CountEntriesWithName(&rtrans, | |
1907 parent_id_, | |
1908 parent2_name)); | |
1909 | |
1910 // Parent2 was a local ID and thus should have changed on commit! | |
1911 Entry pre_commit_entry_parent2(&rtrans, syncable::GET_BY_ID, parent2_id); | |
1912 ASSERT_FALSE(pre_commit_entry_parent2.good()); | |
1913 | |
1914 // Look up the new ID. | |
1915 Id parent2_committed_id = | |
1916 GetOnlyEntryWithName(&rtrans, parent_id_, parent2_name); | |
1917 EXPECT_TRUE(parent2_committed_id.ServerKnows()); | |
1918 | |
1919 Entry child(&rtrans, syncable::GET_BY_ID, child_id); | |
1920 EXPECT_EQ(parent2_committed_id, child.GetParentId()); | |
1921 } | |
1922 } | |
1923 | |
1924 TEST_F(SyncerTest, TestCommitListOrderingAndNewParentAndChild) { | |
1925 string parent_name = "1"; | |
1926 string parent2_name = "A"; | |
1927 string child_name = "B"; | |
1928 | |
1929 { | |
1930 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1931 MutableEntry parent(&wtrans, | |
1932 CREATE, BOOKMARKS, | |
1933 wtrans.root_id(), | |
1934 parent_name); | |
1935 ASSERT_TRUE(parent.good()); | |
1936 parent.PutIsUnsynced(true); | |
1937 parent.PutIsDir(true); | |
1938 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
1939 parent.PutId(parent_id_); | |
1940 parent.PutBaseVersion(1); | |
1941 } | |
1942 | |
1943 int64_t meta_handle_b; | |
1944 const Id parent2_local_id = ids_.NewLocalId(); | |
1945 const Id child_local_id = ids_.NewLocalId(); | |
1946 { | |
1947 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
1948 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, parent_id_, parent2_name); | |
1949 ASSERT_TRUE(parent2.good()); | |
1950 parent2.PutIsUnsynced(true); | |
1951 parent2.PutIsDir(true); | |
1952 parent2.PutSpecifics(DefaultBookmarkSpecifics()); | |
1953 | |
1954 parent2.PutId(parent2_local_id); | |
1955 MutableEntry child( | |
1956 &wtrans, CREATE, BOOKMARKS, parent2_local_id, child_name); | |
1957 ASSERT_TRUE(child.good()); | |
1958 child.PutIsUnsynced(true); | |
1959 child.PutIsDir(true); | |
1960 child.PutSpecifics(DefaultBookmarkSpecifics()); | |
1961 child.PutId(child_local_id); | |
1962 meta_handle_b = child.GetMetahandle(); | |
1963 } | |
1964 | |
1965 EXPECT_TRUE(SyncShareNudge()); | |
1966 ASSERT_EQ(3u, mock_server_->committed_ids().size()); | |
1967 // If this test starts failing, be aware other sort orders could be valid. | |
1968 EXPECT_EQ(parent_id_, mock_server_->committed_ids()[0]); | |
1969 EXPECT_EQ(parent2_local_id, mock_server_->committed_ids()[1]); | |
1970 EXPECT_EQ(child_local_id, mock_server_->committed_ids()[2]); | |
1971 { | |
1972 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
1973 | |
1974 Entry parent(&rtrans, syncable::GET_BY_ID, | |
1975 GetOnlyEntryWithName(&rtrans, rtrans.root_id(), parent_name)); | |
1976 ASSERT_TRUE(parent.good()); | |
1977 EXPECT_TRUE(parent.GetId().ServerKnows()); | |
1978 | |
1979 Entry parent2(&rtrans, syncable::GET_BY_ID, | |
1980 GetOnlyEntryWithName(&rtrans, parent.GetId(), parent2_name)); | |
1981 ASSERT_TRUE(parent2.good()); | |
1982 EXPECT_TRUE(parent2.GetId().ServerKnows()); | |
1983 | |
1984 // Id changed on commit, so this should fail. | |
1985 Entry local_parent2_id_entry(&rtrans, | |
1986 syncable::GET_BY_ID, | |
1987 parent2_local_id); | |
1988 ASSERT_FALSE(local_parent2_id_entry.good()); | |
1989 | |
1990 Entry entry_b(&rtrans, syncable::GET_BY_HANDLE, meta_handle_b); | |
1991 EXPECT_TRUE(entry_b.GetId().ServerKnows()); | |
1992 EXPECT_TRUE(parent2.GetId() == entry_b.GetParentId()); | |
1993 } | |
1994 } | |
1995 | |
1996 TEST_F(SyncerTest, UpdateWithZeroLengthName) { | |
1997 // One illegal update | |
1998 mock_server_->AddUpdateDirectory( | |
1999 1, 0, std::string(), 1, 10, foreign_cache_guid(), "-1"); | |
2000 // And one legal one that we're going to delete. | |
2001 mock_server_->AddUpdateDirectory(2, 0, "FOO", 1, 10, | |
2002 foreign_cache_guid(), "-2"); | |
2003 EXPECT_TRUE(SyncShareNudge()); | |
2004 // Delete the legal one. The new update has a null name. | |
2005 mock_server_->AddUpdateDirectory( | |
2006 2, 0, std::string(), 2, 20, foreign_cache_guid(), "-2"); | |
2007 mock_server_->SetLastUpdateDeleted(); | |
2008 EXPECT_TRUE(SyncShareNudge()); | |
2009 } | |
2010 | |
2011 TEST_F(SyncerTest, TestBasicUpdate) { | |
2012 string id = "some_id"; | |
2013 string parent_id = "0"; | |
2014 string name = "in_root"; | |
2015 int64_t version = 10; | |
2016 int64_t timestamp = 10; | |
2017 mock_server_->AddUpdateDirectory(id, parent_id, name, version, timestamp, | |
2018 foreign_cache_guid(), "-1"); | |
2019 | |
2020 EXPECT_TRUE(SyncShareNudge()); | |
2021 { | |
2022 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2023 Entry entry(&trans, GET_BY_ID, | |
2024 syncable::Id::CreateFromServerId("some_id")); | |
2025 ASSERT_TRUE(entry.good()); | |
2026 EXPECT_TRUE(entry.GetIsDir()); | |
2027 EXPECT_EQ(version, entry.GetServerVersion()); | |
2028 EXPECT_EQ(version, entry.GetBaseVersion()); | |
2029 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
2030 EXPECT_FALSE(entry.GetIsUnsynced()); | |
2031 EXPECT_FALSE(entry.GetServerIsDel()); | |
2032 EXPECT_FALSE(entry.GetIsDel()); | |
2033 } | |
2034 } | |
2035 | |
2036 TEST_F(SyncerTest, IllegalAndLegalUpdates) { | |
2037 Id root = TestIdFactory::root(); | |
2038 // Should apply just fine. | |
2039 mock_server_->AddUpdateDirectory(1, 0, "in_root", 10, 10, | |
2040 foreign_cache_guid(), "-1"); | |
2041 | |
2042 // Same name. But this SHOULD work. | |
2043 mock_server_->AddUpdateDirectory(2, 0, "in_root", 10, 10, | |
2044 foreign_cache_guid(), "-2"); | |
2045 | |
2046 // Unknown parent: should never be applied. "-80" is a legal server ID, | |
2047 // because any string sent by the server is a legal server ID in the sync | |
2048 // protocol, but it's not the ID of any item known to the client. This | |
2049 // update should succeed validation, but be stuck in the unapplied state | |
2050 // until an item with the server ID "-80" arrives. | |
2051 mock_server_->AddUpdateDirectory(3, -80, "bad_parent", 10, 10, | |
2052 foreign_cache_guid(), "-3"); | |
2053 | |
2054 EXPECT_TRUE(SyncShareNudge()); | |
2055 | |
2056 // Id 3 should be in conflict now. | |
2057 EXPECT_EQ( | |
2058 1, | |
2059 GetUpdateCounters(BOOKMARKS).num_hierarchy_conflict_application_failures); | |
2060 | |
2061 // The only request in that loop should have been a GetUpdate. | |
2062 // At that point, we didn't know whether or not we had conflicts. | |
2063 ASSERT_TRUE(mock_server_->last_request().has_get_updates()); | |
2064 VerifyHierarchyConflictsUnspecified(mock_server_->last_request()); | |
2065 | |
2066 // These entries will be used in the second set of updates. | |
2067 mock_server_->AddUpdateDirectory(4, 0, "newer_version", 20, 10, | |
2068 foreign_cache_guid(), "-4"); | |
2069 mock_server_->AddUpdateDirectory(5, 0, "circular1", 10, 10, | |
2070 foreign_cache_guid(), "-5"); | |
2071 mock_server_->AddUpdateDirectory(6, 5, "circular2", 10, 10, | |
2072 foreign_cache_guid(), "-6"); | |
2073 mock_server_->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10, | |
2074 foreign_cache_guid(), "-9"); | |
2075 mock_server_->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10, | |
2076 foreign_cache_guid(), "-100"); | |
2077 mock_server_->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10, | |
2078 foreign_cache_guid(), "-10"); | |
2079 | |
2080 EXPECT_TRUE(SyncShareNudge()); | |
2081 // The three items with an unresolved parent should be unapplied (3, 9, 100). | |
2082 // The name clash should also still be in conflict. | |
2083 EXPECT_EQ( | |
2084 3, | |
2085 GetUpdateCounters(BOOKMARKS).num_hierarchy_conflict_application_failures); | |
2086 | |
2087 // This time around, we knew that there were conflicts. | |
2088 ASSERT_TRUE(mock_server_->last_request().has_get_updates()); | |
2089 VerifyHierarchyConflictsReported(mock_server_->last_request()); | |
2090 | |
2091 { | |
2092 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2093 // Even though it has the same name, it should work. | |
2094 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
2095 ASSERT_TRUE(name_clash.good()); | |
2096 EXPECT_FALSE(name_clash.GetIsUnappliedUpdate()) | |
2097 << "Duplicate name SHOULD be OK."; | |
2098 | |
2099 Entry bad_parent(&trans, GET_BY_ID, ids_.FromNumber(3)); | |
2100 ASSERT_TRUE(bad_parent.good()); | |
2101 EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate()) | |
2102 << "child of unknown parent should be in conflict"; | |
2103 | |
2104 Entry bad_parent_child(&trans, GET_BY_ID, ids_.FromNumber(9)); | |
2105 ASSERT_TRUE(bad_parent_child.good()); | |
2106 EXPECT_TRUE(bad_parent_child.GetIsUnappliedUpdate()) | |
2107 << "grandchild of unknown parent should be in conflict"; | |
2108 | |
2109 Entry bad_parent_child2(&trans, GET_BY_ID, ids_.FromNumber(100)); | |
2110 ASSERT_TRUE(bad_parent_child2.good()); | |
2111 EXPECT_TRUE(bad_parent_child2.GetIsUnappliedUpdate()) | |
2112 << "great-grandchild of unknown parent should be in conflict"; | |
2113 } | |
2114 | |
2115 // Updating 1 should not affect item 2 of the same name. | |
2116 mock_server_->AddUpdateDirectory(1, 0, "new_name", 20, 20, | |
2117 foreign_cache_guid(), "-1"); | |
2118 | |
2119 // Moving 5 under 6 will create a cycle: a conflict. | |
2120 mock_server_->AddUpdateDirectory(5, 6, "circular3", 20, 20, | |
2121 foreign_cache_guid(), "-5"); | |
2122 | |
2123 // Flip the is_dir bit: should fail verify & be dropped. | |
2124 mock_server_->AddUpdateBookmark(10, 0, "dir_to_bookmark", 20, 20, | |
2125 foreign_cache_guid(), "-10"); | |
2126 EXPECT_TRUE(SyncShareNudge()); | |
2127 | |
2128 // Version number older than last known: should fail verify & be dropped. | |
2129 mock_server_->AddUpdateDirectory(4, 0, "old_version", 10, 10, | |
2130 foreign_cache_guid(), "-4"); | |
2131 EXPECT_TRUE(SyncShareNudge()); | |
2132 { | |
2133 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2134 | |
2135 Entry still_a_dir(&trans, GET_BY_ID, ids_.FromNumber(10)); | |
2136 ASSERT_TRUE(still_a_dir.good()); | |
2137 EXPECT_FALSE(still_a_dir.GetIsUnappliedUpdate()); | |
2138 EXPECT_EQ(10u, still_a_dir.GetBaseVersion()); | |
2139 EXPECT_EQ(10u, still_a_dir.GetServerVersion()); | |
2140 EXPECT_TRUE(still_a_dir.GetIsDir()); | |
2141 | |
2142 Entry rename(&trans, GET_BY_ID, ids_.FromNumber(1)); | |
2143 ASSERT_TRUE(rename.good()); | |
2144 EXPECT_EQ(root, rename.GetParentId()); | |
2145 EXPECT_EQ("new_name", rename.GetNonUniqueName()); | |
2146 EXPECT_FALSE(rename.GetIsUnappliedUpdate()); | |
2147 EXPECT_TRUE(ids_.FromNumber(1) == rename.GetId()); | |
2148 EXPECT_EQ(20u, rename.GetBaseVersion()); | |
2149 | |
2150 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
2151 ASSERT_TRUE(name_clash.good()); | |
2152 EXPECT_EQ(root, name_clash.GetParentId()); | |
2153 EXPECT_TRUE(ids_.FromNumber(2) == name_clash.GetId()); | |
2154 EXPECT_EQ(10u, name_clash.GetBaseVersion()); | |
2155 EXPECT_EQ("in_root", name_clash.GetNonUniqueName()); | |
2156 | |
2157 Entry ignored_old_version(&trans, GET_BY_ID, ids_.FromNumber(4)); | |
2158 ASSERT_TRUE(ignored_old_version.good()); | |
2159 EXPECT_EQ("newer_version", ignored_old_version.GetNonUniqueName()); | |
2160 EXPECT_FALSE(ignored_old_version.GetIsUnappliedUpdate()); | |
2161 EXPECT_EQ(20u, ignored_old_version.GetBaseVersion()); | |
2162 | |
2163 Entry circular_parent_issue(&trans, GET_BY_ID, ids_.FromNumber(5)); | |
2164 ASSERT_TRUE(circular_parent_issue.good()); | |
2165 EXPECT_TRUE(circular_parent_issue.GetIsUnappliedUpdate()) | |
2166 << "circular move should be in conflict"; | |
2167 EXPECT_EQ(root_id_, circular_parent_issue.GetParentId()); | |
2168 EXPECT_EQ(ids_.FromNumber(6), circular_parent_issue.GetServerParentId()); | |
2169 EXPECT_EQ(10u, circular_parent_issue.GetBaseVersion()); | |
2170 | |
2171 Entry circular_parent_target(&trans, GET_BY_ID, ids_.FromNumber(6)); | |
2172 ASSERT_TRUE(circular_parent_target.good()); | |
2173 EXPECT_FALSE(circular_parent_target.GetIsUnappliedUpdate()); | |
2174 EXPECT_EQ(circular_parent_issue.GetId(), | |
2175 circular_parent_target.GetParentId()); | |
2176 EXPECT_EQ(10u, circular_parent_target.GetBaseVersion()); | |
2177 } | |
2178 | |
2179 EXPECT_EQ( | |
2180 4, | |
2181 GetUpdateCounters(BOOKMARKS).num_hierarchy_conflict_application_failures); | |
2182 } | |
2183 | |
2184 // A commit with a lost response produces an update that has to be reunited with | |
2185 // its parent. | |
2186 TEST_F(SyncerTest, CommitReuniteUpdateAdjustsChildren) { | |
2187 // Create a folder in the root. | |
2188 int64_t metahandle_folder; | |
2189 { | |
2190 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2191 MutableEntry entry( | |
2192 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder"); | |
2193 ASSERT_TRUE(entry.good()); | |
2194 entry.PutIsDir(true); | |
2195 entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2196 entry.PutIsUnsynced(true); | |
2197 metahandle_folder = entry.GetMetahandle(); | |
2198 } | |
2199 | |
2200 // Verify it and pull the ID out of the folder. | |
2201 syncable::Id folder_id; | |
2202 int64_t metahandle_entry; | |
2203 { | |
2204 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2205 Entry entry(&trans, GET_BY_HANDLE, metahandle_folder); | |
2206 ASSERT_TRUE(entry.good()); | |
2207 folder_id = entry.GetId(); | |
2208 ASSERT_TRUE(!folder_id.ServerKnows()); | |
2209 } | |
2210 | |
2211 // Create an entry in the newly created folder. | |
2212 { | |
2213 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2214 MutableEntry entry(&trans, CREATE, BOOKMARKS, folder_id, "new_entry"); | |
2215 ASSERT_TRUE(entry.good()); | |
2216 metahandle_entry = entry.GetMetahandle(); | |
2217 WriteTestDataToEntry(&trans, &entry); | |
2218 } | |
2219 | |
2220 // Verify it and pull the ID out of the entry. | |
2221 syncable::Id entry_id; | |
2222 { | |
2223 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2224 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry); | |
2225 ASSERT_TRUE(entry.good()); | |
2226 EXPECT_EQ(folder_id, entry.GetParentId()); | |
2227 EXPECT_EQ("new_entry", entry.GetNonUniqueName()); | |
2228 entry_id = entry.GetId(); | |
2229 EXPECT_TRUE(!entry_id.ServerKnows()); | |
2230 VerifyTestDataInEntry(&trans, &entry); | |
2231 } | |
2232 | |
2233 // Now, to emulate a commit response failure, we just don't commit it. | |
2234 int64_t new_version = 150; // any larger value. | |
2235 int64_t timestamp = 20; // arbitrary value. | |
2236 syncable::Id new_folder_id = | |
2237 syncable::Id::CreateFromServerId("folder_server_id"); | |
2238 | |
2239 // The following update should cause the folder to both apply the update, as | |
2240 // well as reassociate the id. | |
2241 mock_server_->AddUpdateDirectory(new_folder_id, root_id_, | |
2242 "new_folder", new_version, timestamp, | |
2243 local_cache_guid(), folder_id.GetServerId()); | |
2244 | |
2245 // We don't want it accidentally committed, just the update applied. | |
2246 mock_server_->set_conflict_all_commits(true); | |
2247 | |
2248 // Alright! Apply that update! | |
2249 EXPECT_FALSE(SyncShareNudge()); | |
2250 { | |
2251 // The folder's ID should have been updated. | |
2252 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2253 Entry folder(&trans, GET_BY_HANDLE, metahandle_folder); | |
2254 ASSERT_TRUE(folder.good()); | |
2255 EXPECT_EQ("new_folder", folder.GetNonUniqueName()); | |
2256 EXPECT_EQ(new_version, folder.GetBaseVersion()); | |
2257 EXPECT_EQ(new_folder_id, folder.GetId()); | |
2258 EXPECT_TRUE(folder.GetId().ServerKnows()); | |
2259 EXPECT_EQ(trans.root_id(), folder.GetParentId()); | |
2260 | |
2261 // Since it was updated, the old folder should not exist. | |
2262 Entry old_dead_folder(&trans, GET_BY_ID, folder_id); | |
2263 EXPECT_FALSE(old_dead_folder.good()); | |
2264 | |
2265 // The child's parent should have changed. | |
2266 Entry entry(&trans, syncable::GET_BY_HANDLE, metahandle_entry); | |
2267 ASSERT_TRUE(entry.good()); | |
2268 EXPECT_EQ("new_entry", entry.GetNonUniqueName()); | |
2269 EXPECT_EQ(new_folder_id, entry.GetParentId()); | |
2270 EXPECT_TRUE(!entry.GetId().ServerKnows()); | |
2271 VerifyTestDataInEntry(&trans, &entry); | |
2272 } | |
2273 } | |
2274 | |
2275 // A commit with a lost response produces an update that has to be reunited with | |
2276 // its parent. | |
2277 TEST_F(SyncerTest, CommitReuniteUpdate) { | |
2278 // Create an entry in the root. | |
2279 int64_t entry_metahandle; | |
2280 { | |
2281 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2282 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry"); | |
2283 ASSERT_TRUE(entry.good()); | |
2284 entry_metahandle = entry.GetMetahandle(); | |
2285 WriteTestDataToEntry(&trans, &entry); | |
2286 } | |
2287 | |
2288 // Verify it and pull the ID out. | |
2289 syncable::Id entry_id; | |
2290 { | |
2291 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2292 | |
2293 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle); | |
2294 ASSERT_TRUE(entry.good()); | |
2295 entry_id = entry.GetId(); | |
2296 EXPECT_TRUE(!entry_id.ServerKnows()); | |
2297 VerifyTestDataInEntry(&trans, &entry); | |
2298 } | |
2299 | |
2300 // Now, to emulate a commit response failure, we just don't commit it. | |
2301 int64_t new_version = 150; // any larger value. | |
2302 int64_t timestamp = 20; // arbitrary value. | |
2303 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id"); | |
2304 | |
2305 // Generate an update from the server with a relevant ID reassignment. | |
2306 mock_server_->AddUpdateBookmark(new_entry_id, root_id_, | |
2307 "new_entry", new_version, timestamp, | |
2308 local_cache_guid(), entry_id.GetServerId()); | |
2309 | |
2310 // We don't want it accidentally committed, just the update applied. | |
2311 mock_server_->set_conflict_all_commits(true); | |
2312 | |
2313 // Alright! Apply that update! | |
2314 EXPECT_TRUE(SyncShareNudge()); | |
2315 { | |
2316 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2317 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle); | |
2318 ASSERT_TRUE(entry.good()); | |
2319 EXPECT_EQ(new_version, entry.GetBaseVersion()); | |
2320 EXPECT_EQ(new_entry_id, entry.GetId()); | |
2321 EXPECT_EQ("new_entry", entry.GetNonUniqueName()); | |
2322 } | |
2323 } | |
2324 | |
2325 // A commit with a lost response must work even if the local entry was deleted | |
2326 // before the update is applied. We should not duplicate the local entry in | |
2327 // this case, but just create another one alongside. We may wish to examine | |
2328 // this behavior in the future as it can create hanging uploads that never | |
2329 // finish, that must be cleaned up on the server side after some time. | |
2330 TEST_F(SyncerTest, CommitReuniteUpdateDoesNotChokeOnDeletedLocalEntry) { | |
2331 // Create a entry in the root. | |
2332 int64_t entry_metahandle; | |
2333 { | |
2334 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2335 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "new_entry"); | |
2336 ASSERT_TRUE(entry.good()); | |
2337 entry_metahandle = entry.GetMetahandle(); | |
2338 WriteTestDataToEntry(&trans, &entry); | |
2339 } | |
2340 // Verify it and pull the ID out. | |
2341 syncable::Id entry_id; | |
2342 { | |
2343 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2344 Entry entry(&trans, GET_BY_HANDLE, entry_metahandle); | |
2345 ASSERT_TRUE(entry.good()); | |
2346 entry_id = entry.GetId(); | |
2347 EXPECT_TRUE(!entry_id.ServerKnows()); | |
2348 VerifyTestDataInEntry(&trans, &entry); | |
2349 } | |
2350 | |
2351 // Now, to emulate a commit response failure, we just don't commit it. | |
2352 int64_t new_version = 150; // any larger value. | |
2353 int64_t timestamp = 20; // arbitrary value. | |
2354 syncable::Id new_entry_id = syncable::Id::CreateFromServerId("server_id"); | |
2355 | |
2356 // Generate an update from the server with a relevant ID reassignment. | |
2357 mock_server_->AddUpdateBookmark(new_entry_id, root_id_, | |
2358 "new_entry", new_version, timestamp, | |
2359 local_cache_guid(), entry_id.GetServerId()); | |
2360 | |
2361 // We don't want it accidentally committed, just the update applied. | |
2362 mock_server_->set_conflict_all_commits(true); | |
2363 | |
2364 // Purposefully delete the entry now before the update application finishes. | |
2365 { | |
2366 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2367 Id new_entry_id = GetOnlyEntryWithName( | |
2368 &trans, trans.root_id(), "new_entry"); | |
2369 MutableEntry entry(&trans, GET_BY_ID, new_entry_id); | |
2370 ASSERT_TRUE(entry.good()); | |
2371 entry.PutIsDel(true); | |
2372 } | |
2373 | |
2374 // Just don't CHECK fail in sync, have the update split. | |
2375 EXPECT_TRUE(SyncShareNudge()); | |
2376 { | |
2377 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2378 Id new_entry_id = GetOnlyEntryWithName( | |
2379 &trans, trans.root_id(), "new_entry"); | |
2380 Entry entry(&trans, GET_BY_ID, new_entry_id); | |
2381 ASSERT_TRUE(entry.good()); | |
2382 EXPECT_FALSE(entry.GetIsDel()); | |
2383 | |
2384 Entry old_entry(&trans, GET_BY_ID, entry_id); | |
2385 ASSERT_TRUE(old_entry.good()); | |
2386 EXPECT_TRUE(old_entry.GetIsDel()); | |
2387 } | |
2388 } | |
2389 | |
2390 // TODO(chron): Add more unsanitized name tests. | |
2391 TEST_F(SyncerTest, ConflictMatchingEntryHandlesUnsanitizedNames) { | |
2392 mock_server_->AddUpdateDirectory(1, 0, "A/A", 10, 10, | |
2393 foreign_cache_guid(), "-1"); | |
2394 mock_server_->AddUpdateDirectory(2, 0, "B/B", 10, 10, | |
2395 foreign_cache_guid(), "-2"); | |
2396 mock_server_->set_conflict_all_commits(true); | |
2397 EXPECT_TRUE(SyncShareNudge()); | |
2398 { | |
2399 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
2400 | |
2401 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
2402 ASSERT_TRUE(A.good()); | |
2403 A.PutIsUnsynced(true); | |
2404 A.PutIsUnappliedUpdate(true); | |
2405 A.PutServerVersion(20); | |
2406 | |
2407 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); | |
2408 ASSERT_TRUE(B.good()); | |
2409 B.PutIsUnappliedUpdate(true); | |
2410 B.PutServerVersion(20); | |
2411 } | |
2412 EXPECT_TRUE(SyncShareNudge()); | |
2413 mock_server_->set_conflict_all_commits(false); | |
2414 | |
2415 { | |
2416 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2417 | |
2418 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1)); | |
2419 ASSERT_TRUE(A.good()); | |
2420 EXPECT_FALSE(A.GetIsUnsynced()); | |
2421 EXPECT_FALSE(A.GetIsUnappliedUpdate()); | |
2422 EXPECT_EQ(20, A.GetServerVersion()); | |
2423 | |
2424 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
2425 ASSERT_TRUE(B.good()); | |
2426 EXPECT_FALSE(B.GetIsUnsynced()); | |
2427 EXPECT_FALSE(B.GetIsUnappliedUpdate()); | |
2428 EXPECT_EQ(20, B.GetServerVersion()); | |
2429 } | |
2430 } | |
2431 | |
2432 TEST_F(SyncerTest, ConflictMatchingEntryHandlesNormalNames) { | |
2433 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10, | |
2434 foreign_cache_guid(), "-1"); | |
2435 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10, | |
2436 foreign_cache_guid(), "-2"); | |
2437 mock_server_->set_conflict_all_commits(true); | |
2438 EXPECT_TRUE(SyncShareNudge()); | |
2439 { | |
2440 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
2441 | |
2442 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
2443 ASSERT_TRUE(A.good()); | |
2444 A.PutIsUnsynced(true); | |
2445 A.PutIsUnappliedUpdate(true); | |
2446 A.PutServerVersion(20); | |
2447 | |
2448 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); | |
2449 ASSERT_TRUE(B.good()); | |
2450 B.PutIsUnappliedUpdate(true); | |
2451 B.PutServerVersion(20); | |
2452 } | |
2453 EXPECT_TRUE(SyncShareNudge()); | |
2454 mock_server_->set_conflict_all_commits(false); | |
2455 | |
2456 { | |
2457 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2458 | |
2459 Entry A(&trans, GET_BY_ID, ids_.FromNumber(1)); | |
2460 ASSERT_TRUE(A.good()); | |
2461 EXPECT_FALSE(A.GetIsUnsynced()); | |
2462 EXPECT_FALSE(A.GetIsUnappliedUpdate()); | |
2463 EXPECT_EQ(20, A.GetServerVersion()); | |
2464 | |
2465 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
2466 ASSERT_TRUE(B.good()); | |
2467 EXPECT_FALSE(B.GetIsUnsynced()); | |
2468 EXPECT_FALSE(B.GetIsUnappliedUpdate()); | |
2469 EXPECT_EQ(20, B.GetServerVersion()); | |
2470 } | |
2471 } | |
2472 | |
2473 TEST_F(SyncerTest, ReverseFolderOrderingTest) { | |
2474 mock_server_->AddUpdateDirectory(4, 3, "ggchild", 10, 10, | |
2475 foreign_cache_guid(), "-4"); | |
2476 mock_server_->AddUpdateDirectory(3, 2, "gchild", 10, 10, | |
2477 foreign_cache_guid(), "-3"); | |
2478 mock_server_->AddUpdateDirectory(5, 4, "gggchild", 10, 10, | |
2479 foreign_cache_guid(), "-5"); | |
2480 mock_server_->AddUpdateDirectory(2, 1, "child", 10, 10, | |
2481 foreign_cache_guid(), "-2"); | |
2482 mock_server_->AddUpdateDirectory(1, 0, "parent", 10, 10, | |
2483 foreign_cache_guid(), "-1"); | |
2484 EXPECT_TRUE(SyncShareNudge()); | |
2485 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2486 | |
2487 Id child_id = GetOnlyEntryWithName( | |
2488 &trans, ids_.FromNumber(4), "gggchild"); | |
2489 Entry child(&trans, GET_BY_ID, child_id); | |
2490 ASSERT_TRUE(child.good()); | |
2491 } | |
2492 | |
2493 class EntryCreatedInNewFolderTest : public SyncerTest { | |
2494 public: | |
2495 void CreateFolderInBob() { | |
2496 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2497 MutableEntry bob(&trans, | |
2498 syncable::GET_BY_ID, | |
2499 GetOnlyEntryWithName(&trans, | |
2500 TestIdFactory::root(), | |
2501 "bob")); | |
2502 ASSERT_TRUE(bob.good()); | |
2503 | |
2504 MutableEntry entry2( | |
2505 &trans, CREATE, BOOKMARKS, bob.GetId(), "bob"); | |
2506 ASSERT_TRUE(entry2.good()); | |
2507 entry2.PutIsDir(true); | |
2508 entry2.PutIsUnsynced(true); | |
2509 entry2.PutSpecifics(DefaultBookmarkSpecifics()); | |
2510 } | |
2511 }; | |
2512 | |
2513 TEST_F(EntryCreatedInNewFolderTest, EntryCreatedInNewFolderMidSync) { | |
2514 { | |
2515 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2516 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob"); | |
2517 ASSERT_TRUE(entry.good()); | |
2518 entry.PutIsDir(true); | |
2519 entry.PutIsUnsynced(true); | |
2520 entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2521 } | |
2522 | |
2523 mock_server_->SetMidCommitCallback( | |
2524 base::Bind(&EntryCreatedInNewFolderTest::CreateFolderInBob, | |
2525 base::Unretained(this))); | |
2526 EXPECT_TRUE(SyncShareNudge()); | |
2527 // We loop until no unsynced handles remain, so we will commit both ids. | |
2528 EXPECT_EQ(2u, mock_server_->committed_ids().size()); | |
2529 { | |
2530 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2531 Entry parent_entry(&trans, syncable::GET_BY_ID, | |
2532 GetOnlyEntryWithName(&trans, TestIdFactory::root(), "bob")); | |
2533 ASSERT_TRUE(parent_entry.good()); | |
2534 | |
2535 Id child_id = | |
2536 GetOnlyEntryWithName(&trans, parent_entry.GetId(), "bob"); | |
2537 Entry child(&trans, syncable::GET_BY_ID, child_id); | |
2538 ASSERT_TRUE(child.good()); | |
2539 EXPECT_EQ(parent_entry.GetId(), child.GetParentId()); | |
2540 } | |
2541 } | |
2542 | |
2543 TEST_F(SyncerTest, NegativeIDInUpdate) { | |
2544 mock_server_->AddUpdateBookmark(-10, 0, "bad", 40, 40, | |
2545 foreign_cache_guid(), "-100"); | |
2546 EXPECT_TRUE(SyncShareNudge()); | |
2547 // The negative id would make us CHECK! | |
2548 } | |
2549 | |
2550 TEST_F(SyncerTest, UnappliedUpdateOnCreatedItemItemDoesNotCrash) { | |
2551 int64_t metahandle_fred; | |
2552 syncable::Id orig_id; | |
2553 { | |
2554 // Create an item. | |
2555 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2556 MutableEntry fred_match(&trans, CREATE, BOOKMARKS, trans.root_id(), | |
2557 "fred_match"); | |
2558 ASSERT_TRUE(fred_match.good()); | |
2559 metahandle_fred = fred_match.GetMetahandle(); | |
2560 orig_id = fred_match.GetId(); | |
2561 WriteTestDataToEntry(&trans, &fred_match); | |
2562 } | |
2563 // Commit it. | |
2564 EXPECT_TRUE(SyncShareNudge()); | |
2565 EXPECT_EQ(1u, mock_server_->committed_ids().size()); | |
2566 mock_server_->set_conflict_all_commits(true); | |
2567 syncable::Id fred_match_id; | |
2568 { | |
2569 // Now receive a change from outside. | |
2570 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2571 MutableEntry fred_match(&trans, GET_BY_HANDLE, metahandle_fred); | |
2572 ASSERT_TRUE(fred_match.good()); | |
2573 EXPECT_TRUE(fred_match.GetId().ServerKnows()); | |
2574 fred_match_id = fred_match.GetId(); | |
2575 mock_server_->AddUpdateBookmark(fred_match_id, trans.root_id(), | |
2576 "fred_match", 40, 40, local_cache_guid(), orig_id.GetServerId()); | |
2577 } | |
2578 // Run the syncer. | |
2579 for (int i = 0 ; i < 30 ; ++i) { | |
2580 EXPECT_TRUE(SyncShareNudge()); | |
2581 } | |
2582 } | |
2583 | |
2584 /** | |
2585 * In the event that we have a double changed entry, that is changed on both | |
2586 * the client and the server, the conflict resolver should just drop one of | |
2587 * them and accept the other. | |
2588 */ | |
2589 | |
2590 TEST_F(SyncerTest, DoublyChangedWithResolver) { | |
2591 syncable::Id local_id; | |
2592 { | |
2593 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
2594 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder"); | |
2595 ASSERT_TRUE(parent.good()); | |
2596 parent.PutIsDir(true); | |
2597 parent.PutId(parent_id_); | |
2598 parent.PutBaseVersion(5); | |
2599 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
2600 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent_id_, "Pete.htm"); | |
2601 ASSERT_TRUE(child.good()); | |
2602 local_id = child.GetId(); | |
2603 child.PutId(child_id_); | |
2604 child.PutBaseVersion(10); | |
2605 WriteTestDataToEntry(&wtrans, &child); | |
2606 } | |
2607 mock_server_->AddUpdateBookmark(child_id_, parent_id_, "Pete2.htm", 11, 10, | |
2608 local_cache_guid(), local_id.GetServerId()); | |
2609 mock_server_->set_conflict_all_commits(true); | |
2610 EXPECT_FALSE(SyncShareNudge()); | |
2611 syncable::Directory::Metahandles children; | |
2612 { | |
2613 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2614 directory()->GetChildHandlesById(&trans, parent_id_, &children); | |
2615 // We expect the conflict resolver to preserve the local entry. | |
2616 Entry child(&trans, syncable::GET_BY_ID, child_id_); | |
2617 ASSERT_TRUE(child.good()); | |
2618 EXPECT_TRUE(child.GetIsUnsynced()); | |
2619 EXPECT_FALSE(child.GetIsUnappliedUpdate()); | |
2620 EXPECT_TRUE(child.GetSpecifics().has_bookmark()); | |
2621 EXPECT_EQ("Pete.htm", child.GetNonUniqueName()); | |
2622 VerifyTestBookmarkDataInEntry(&child); | |
2623 } | |
2624 | |
2625 // Only one entry, since we just overwrite one. | |
2626 EXPECT_EQ(1u, children.size()); | |
2627 } | |
2628 | |
2629 // We got this repro case when someone was editing bookmarks while sync was | |
2630 // occuring. The entry had changed out underneath the user. | |
2631 TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) { | |
2632 const base::Time& test_time = ProtoTimeToTime(123456); | |
2633 syncable::Id local_id; | |
2634 int64_t entry_metahandle; | |
2635 { | |
2636 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
2637 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, root_id_, "Pete"); | |
2638 ASSERT_TRUE(entry.good()); | |
2639 EXPECT_FALSE(entry.GetId().ServerKnows()); | |
2640 local_id = entry.GetId(); | |
2641 entry.PutIsDir(true); | |
2642 entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2643 entry.PutIsUnsynced(true); | |
2644 entry.PutMtime(test_time); | |
2645 entry_metahandle = entry.GetMetahandle(); | |
2646 } | |
2647 EXPECT_TRUE(SyncShareNudge()); | |
2648 syncable::Id id; | |
2649 int64_t version; | |
2650 { | |
2651 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2652 Entry entry(&trans, syncable::GET_BY_HANDLE, entry_metahandle); | |
2653 ASSERT_TRUE(entry.good()); | |
2654 id = entry.GetId(); | |
2655 EXPECT_TRUE(id.ServerKnows()); | |
2656 version = entry.GetBaseVersion(); | |
2657 } | |
2658 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); | |
2659 update->set_originator_cache_guid(local_cache_guid()); | |
2660 update->set_originator_client_item_id(local_id.GetServerId()); | |
2661 EXPECT_EQ("Pete", update->name()); | |
2662 EXPECT_EQ(id.GetServerId(), update->id_string()); | |
2663 EXPECT_EQ(root_id_.GetServerId(), update->parent_id_string()); | |
2664 EXPECT_EQ(version, update->version()); | |
2665 EXPECT_TRUE(SyncShareNudge()); | |
2666 { | |
2667 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2668 Entry entry(&trans, syncable::GET_BY_ID, id); | |
2669 ASSERT_TRUE(entry.good()); | |
2670 EXPECT_EQ(test_time, entry.GetMtime()); | |
2671 } | |
2672 } | |
2673 | |
2674 TEST_F(SyncerTest, ParentAndChildBothMatch) { | |
2675 // Disable PREFERENCES and EXTENSIONS which are enabled at the setup step to | |
2676 // avoid auto-creating root folders and failing the test below | |
2677 // that verifies the number of children at the root. | |
2678 DisableDatatype(PREFERENCES); | |
2679 DisableDatatype(EXTENSIONS); | |
2680 | |
2681 const FullModelTypeSet all_types = FullModelTypeSet::All(); | |
2682 syncable::Id parent_id = ids_.NewServerId(); | |
2683 syncable::Id child_id = ids_.NewServerId(); | |
2684 syncable::Id parent_local_id; | |
2685 syncable::Id child_local_id; | |
2686 | |
2687 { | |
2688 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
2689 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "Folder"); | |
2690 ASSERT_TRUE(parent.good()); | |
2691 parent_local_id = parent.GetId(); | |
2692 parent.PutIsDir(true); | |
2693 parent.PutIsUnsynced(true); | |
2694 parent.PutId(parent_id); | |
2695 parent.PutBaseVersion(1); | |
2696 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
2697 | |
2698 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "test.htm"); | |
2699 ASSERT_TRUE(child.good()); | |
2700 child_local_id = child.GetId(); | |
2701 child.PutId(child_id); | |
2702 child.PutBaseVersion(1); | |
2703 child.PutSpecifics(DefaultBookmarkSpecifics()); | |
2704 WriteTestDataToEntry(&wtrans, &child); | |
2705 } | |
2706 mock_server_->AddUpdateDirectory(parent_id, root_id_, "Folder", 10, 10, | |
2707 local_cache_guid(), | |
2708 parent_local_id.GetServerId()); | |
2709 mock_server_->AddUpdateBookmark(child_id, parent_id, "test.htm", 10, 10, | |
2710 local_cache_guid(), | |
2711 child_local_id.GetServerId()); | |
2712 mock_server_->set_conflict_all_commits(true); | |
2713 EXPECT_TRUE(SyncShareNudge()); | |
2714 EXPECT_TRUE(SyncShareNudge()); | |
2715 EXPECT_TRUE(SyncShareNudge()); | |
2716 { | |
2717 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2718 Directory::Metahandles children; | |
2719 directory()->GetChildHandlesById(&trans, root_id_, &children); | |
2720 EXPECT_EQ(1u, children.size()); | |
2721 directory()->GetChildHandlesById(&trans, parent_id, &children); | |
2722 EXPECT_EQ(1u, children.size()); | |
2723 std::vector<int64_t> unapplied; | |
2724 directory()->GetUnappliedUpdateMetaHandles(&trans, all_types, &unapplied); | |
2725 EXPECT_EQ(0u, unapplied.size()); | |
2726 syncable::Directory::Metahandles unsynced; | |
2727 directory()->GetUnsyncedMetaHandles(&trans, &unsynced); | |
2728 EXPECT_EQ(0u, unsynced.size()); | |
2729 } | |
2730 } | |
2731 | |
2732 TEST_F(SyncerTest, CommittingNewDeleted) { | |
2733 { | |
2734 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2735 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob"); | |
2736 entry.PutIsUnsynced(true); | |
2737 entry.PutIsDel(true); | |
2738 } | |
2739 EXPECT_TRUE(SyncShareNudge()); | |
2740 EXPECT_EQ(0u, mock_server_->committed_ids().size()); | |
2741 } | |
2742 | |
2743 // Original problem synopsis: | |
2744 // Check failed: entry->GetBaseVersion()<= entry->GetServerVersion() | |
2745 // Client creates entry, client finishes committing entry. Between | |
2746 // commit and getting update back, we delete the entry. | |
2747 // We get the update for the entry, but the local one was modified | |
2748 // so we store the entry but don't apply it. IS_UNAPPLIED_UPDATE is set. | |
2749 // We commit deletion and get a new version number. | |
2750 // We apply unapplied updates again before we get the update about the deletion. | |
2751 // This means we have an unapplied update where server_version < base_version. | |
2752 TEST_F(SyncerTest, UnappliedUpdateDuringCommit) { | |
2753 // This test is a little fake. | |
2754 { | |
2755 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2756 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob"); | |
2757 entry.PutId(ids_.FromNumber(20)); | |
2758 entry.PutBaseVersion(1); | |
2759 entry.PutServerVersion(1); | |
2760 entry.PutServerParentId(ids_.FromNumber(9999)); // Bad parent. | |
2761 entry.PutIsUnsynced(true); | |
2762 entry.PutIsUnappliedUpdate(true); | |
2763 entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2764 entry.PutServerSpecifics(DefaultBookmarkSpecifics()); | |
2765 entry.PutIsDel(false); | |
2766 } | |
2767 EXPECT_TRUE(SyncShareNudge()); | |
2768 EXPECT_EQ(1, session_->status_controller().TotalNumConflictingItems()); | |
2769 } | |
2770 | |
2771 // Original problem synopsis: | |
2772 // Illegal parent | |
2773 // Unexpected error during sync if we: | |
2774 // make a new folder bob | |
2775 // wait for sync | |
2776 // make a new folder fred | |
2777 // move bob into fred | |
2778 // remove bob | |
2779 // remove fred | |
2780 // if no syncing occured midway, bob will have an illegal parent | |
2781 TEST_F(SyncerTest, DeletingEntryInFolder) { | |
2782 // This test is a little fake. | |
2783 int64_t existing_metahandle; | |
2784 { | |
2785 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2786 MutableEntry entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "existing"); | |
2787 ASSERT_TRUE(entry.good()); | |
2788 entry.PutIsDir(true); | |
2789 entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2790 entry.PutIsUnsynced(true); | |
2791 existing_metahandle = entry.GetMetahandle(); | |
2792 } | |
2793 EXPECT_TRUE(SyncShareNudge()); | |
2794 { | |
2795 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2796 MutableEntry newfolder(&trans, CREATE, BOOKMARKS, trans.root_id(), "new"); | |
2797 ASSERT_TRUE(newfolder.good()); | |
2798 newfolder.PutIsDir(true); | |
2799 newfolder.PutSpecifics(DefaultBookmarkSpecifics()); | |
2800 newfolder.PutIsUnsynced(true); | |
2801 | |
2802 MutableEntry existing(&trans, GET_BY_HANDLE, existing_metahandle); | |
2803 ASSERT_TRUE(existing.good()); | |
2804 existing.PutParentId(newfolder.GetId()); | |
2805 existing.PutIsUnsynced(true); | |
2806 EXPECT_TRUE(existing.GetId().ServerKnows()); | |
2807 | |
2808 newfolder.PutIsDel(true); | |
2809 existing.PutIsDel(true); | |
2810 } | |
2811 EXPECT_TRUE(SyncShareNudge()); | |
2812 EXPECT_EQ(0, GetCommitCounters(BOOKMARKS).num_commits_conflict); | |
2813 } | |
2814 | |
2815 // Test conflict resolution when deleting a hierarchy of nodes within a folder | |
2816 // and running into a conflict in one of items. The conflict in a deleted | |
2817 // item must prevent all deleted ancestors from being committed as well; | |
2818 // otherwise the conflicting item would end up being orphaned. | |
2819 TEST_F(SyncerTest, DeletingFolderWithConflictInSubfolder) { | |
2820 int64_t top_handle, nested_handle, leaf_handle; | |
2821 { | |
2822 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2823 MutableEntry top_entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "top"); | |
2824 ASSERT_TRUE(top_entry.good()); | |
2825 top_entry.PutIsDir(true); | |
2826 top_entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2827 top_entry.PutIsUnsynced(true); | |
2828 top_handle = top_entry.GetMetahandle(); | |
2829 | |
2830 MutableEntry nested_entry(&trans, CREATE, BOOKMARKS, top_entry.GetId(), | |
2831 "nested"); | |
2832 ASSERT_TRUE(nested_entry.good()); | |
2833 nested_entry.PutIsDir(true); | |
2834 nested_entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2835 nested_entry.PutIsUnsynced(true); | |
2836 nested_handle = nested_entry.GetMetahandle(); | |
2837 | |
2838 MutableEntry leaf_entry(&trans, CREATE, BOOKMARKS, nested_entry.GetId(), | |
2839 "leaf"); | |
2840 ASSERT_TRUE(leaf_entry.good()); | |
2841 leaf_entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2842 leaf_entry.PutIsUnsynced(true); | |
2843 leaf_handle = leaf_entry.GetMetahandle(); | |
2844 } | |
2845 EXPECT_TRUE(SyncShareNudge()); | |
2846 | |
2847 // Delete all 3 entries and also add unapplied update to the middle one. | |
2848 { | |
2849 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2850 MutableEntry leaf_entry(&trans, GET_BY_HANDLE, leaf_handle); | |
2851 ASSERT_TRUE(leaf_entry.good()); | |
2852 EXPECT_TRUE(leaf_entry.GetId().ServerKnows()); | |
2853 leaf_entry.PutIsUnsynced(true); | |
2854 leaf_entry.PutIsDel(true); | |
2855 | |
2856 MutableEntry nested_entry(&trans, GET_BY_HANDLE, nested_handle); | |
2857 ASSERT_TRUE(nested_entry.good()); | |
2858 EXPECT_TRUE(nested_entry.GetId().ServerKnows()); | |
2859 nested_entry.PutIsUnsynced(true); | |
2860 nested_entry.PutIsDel(true); | |
2861 | |
2862 sync_pb::EntitySpecifics specifics; | |
2863 specifics.mutable_bookmark()->set_url("http://demo/"); | |
2864 specifics.mutable_bookmark()->set_favicon("PNG"); | |
2865 nested_entry.PutServerSpecifics(specifics); | |
2866 // This will put the entry into conflict. | |
2867 nested_entry.PutIsUnappliedUpdate(true); | |
2868 nested_entry.PutServerVersion(nested_entry.GetBaseVersion() + 1); | |
2869 | |
2870 MutableEntry top_entry(&trans, GET_BY_HANDLE, top_handle); | |
2871 ASSERT_TRUE(top_entry.good()); | |
2872 EXPECT_TRUE(top_entry.GetId().ServerKnows()); | |
2873 top_entry.PutIsUnsynced(true); | |
2874 top_entry.PutIsDel(true); | |
2875 } | |
2876 EXPECT_TRUE(SyncShareNudge()); | |
2877 | |
2878 // Verify that the top folder hasn't been committed. Doing so would | |
2879 // orphan the nested folder. | |
2880 syncable::Id top_id; | |
2881 { | |
2882 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2883 Entry top_entry(&trans, GET_BY_HANDLE, top_handle); | |
2884 ASSERT_TRUE(top_entry.good()); | |
2885 top_id = top_entry.GetId(); | |
2886 | |
2887 EXPECT_TRUE(top_entry.GetIsUnsynced()); | |
2888 EXPECT_TRUE(top_entry.GetIsDel()); | |
2889 } | |
2890 | |
2891 EXPECT_THAT(mock_server_->committed_ids(), | |
2892 testing::Not(testing::Contains(top_id))); | |
2893 } | |
2894 | |
2895 // Test conflict resolution when committing a hierarchy of items and running | |
2896 // into a conflict in a parent folder. A conflicting parent must prevent any | |
2897 // of its descendants from being committed. | |
2898 TEST_F(SyncerTest, CommittingItemsWithConflictInParentFolder) { | |
2899 int64_t top_handle, nested_handle, leaf_handle; | |
2900 { | |
2901 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2902 MutableEntry top_entry(&trans, CREATE, BOOKMARKS, trans.root_id(), "top"); | |
2903 ASSERT_TRUE(top_entry.good()); | |
2904 top_entry.PutIsDir(true); | |
2905 top_entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2906 top_entry.PutIsUnsynced(true); | |
2907 top_handle = top_entry.GetMetahandle(); | |
2908 | |
2909 MutableEntry nested_entry(&trans, CREATE, BOOKMARKS, top_entry.GetId(), | |
2910 "nested"); | |
2911 ASSERT_TRUE(nested_entry.good()); | |
2912 nested_entry.PutIsDir(true); | |
2913 nested_entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2914 nested_entry.PutIsUnsynced(true); | |
2915 nested_handle = nested_entry.GetMetahandle(); | |
2916 | |
2917 MutableEntry leaf_entry(&trans, CREATE, BOOKMARKS, nested_entry.GetId(), | |
2918 "leaf"); | |
2919 ASSERT_TRUE(leaf_entry.good()); | |
2920 leaf_entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
2921 leaf_entry.PutIsUnsynced(true); | |
2922 leaf_handle = leaf_entry.GetMetahandle(); | |
2923 } | |
2924 EXPECT_TRUE(SyncShareNudge()); | |
2925 | |
2926 // Touch all 3 entries and also add unapplied update to the top one. | |
2927 syncable::Id top_id, nested_id, leaf_id; | |
2928 { | |
2929 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
2930 sync_pb::EntitySpecifics specifics; | |
2931 specifics.mutable_bookmark()->set_url("http://demo/"); | |
2932 | |
2933 MutableEntry top_entry(&trans, GET_BY_HANDLE, top_handle); | |
2934 ASSERT_TRUE(top_entry.good()); | |
2935 top_id = top_entry.GetId(); | |
2936 EXPECT_TRUE(top_id.ServerKnows()); | |
2937 top_entry.PutIsUnsynced(true); | |
2938 top_entry.PutSpecifics(specifics); | |
2939 | |
2940 // This will put the top entry into conflict. | |
2941 top_entry.PutIsUnappliedUpdate(true); | |
2942 top_entry.PutServerIsDel(true); | |
2943 top_entry.PutServerVersion(top_entry.GetBaseVersion() + 1); | |
2944 | |
2945 MutableEntry nested_entry(&trans, GET_BY_HANDLE, nested_handle); | |
2946 ASSERT_TRUE(nested_entry.good()); | |
2947 nested_id = nested_entry.GetId(); | |
2948 EXPECT_TRUE(nested_id.ServerKnows()); | |
2949 nested_entry.PutSpecifics(specifics); | |
2950 nested_entry.PutIsUnsynced(true); | |
2951 | |
2952 MutableEntry leaf_entry(&trans, GET_BY_HANDLE, leaf_handle); | |
2953 ASSERT_TRUE(leaf_entry.good()); | |
2954 leaf_id = leaf_entry.GetId(); | |
2955 EXPECT_TRUE(leaf_id.ServerKnows()); | |
2956 leaf_entry.PutSpecifics(specifics); | |
2957 leaf_entry.PutIsUnsynced(true); | |
2958 } | |
2959 EXPECT_TRUE(SyncShareNudge()); | |
2960 | |
2961 // Verify that all 3 entries remain unsynced | |
2962 EXPECT_THAT(mock_server_->committed_ids(), | |
2963 testing::Not(testing::Contains(top_id))); | |
2964 EXPECT_THAT(mock_server_->committed_ids(), | |
2965 testing::Not(testing::Contains(nested_id))); | |
2966 EXPECT_THAT(mock_server_->committed_ids(), | |
2967 testing::Not(testing::Contains(leaf_id))); | |
2968 | |
2969 { | |
2970 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
2971 | |
2972 Entry top_entry(&trans, GET_BY_HANDLE, top_handle); | |
2973 ASSERT_TRUE(top_entry.good()); | |
2974 ASSERT_TRUE(top_entry.GetIsUnsynced()); | |
2975 | |
2976 Entry nested_entry(&trans, GET_BY_HANDLE, nested_handle); | |
2977 ASSERT_TRUE(nested_entry.good()); | |
2978 ASSERT_TRUE(nested_entry.GetIsUnsynced()); | |
2979 | |
2980 Entry leaf_entry(&trans, GET_BY_HANDLE, leaf_handle); | |
2981 ASSERT_TRUE(leaf_entry.good()); | |
2982 ASSERT_TRUE(leaf_entry.GetIsUnsynced()); | |
2983 } | |
2984 } | |
2985 | |
2986 // Test conflict resolution when handling an update for an item with specified | |
2987 // Parent ID and having an implicit (unset) Parent ID in the update. | |
2988 TEST_F(SyncerTest, ConflictWithImplicitParent) { | |
2989 // Make sure PREFERENCES root exists so that we can get its parent ID. | |
2990 mock_server_->AddUpdateSpecifics(1, 0, "Folder", 10, 10, true, 1, | |
2991 DefaultPreferencesSpecifics()); | |
2992 mock_server_->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES)); | |
2993 EXPECT_TRUE(SyncShareNudge()); | |
2994 | |
2995 Id pref_root_id; | |
2996 { | |
2997 // Preferences type root should have been created by the update above. | |
2998 // We need it in order to get its ID. | |
2999 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3000 | |
3001 Entry pref_root(&trans, GET_TYPE_ROOT, PREFERENCES); | |
3002 ASSERT_TRUE(pref_root.good()); | |
3003 pref_root_id = pref_root.GetId(); | |
3004 } | |
3005 | |
3006 // Fake an item which is both unsynced and unapplied with | |
3007 // PARENT_ID set to |pref_root_id| and SERVER_PARENT_ID unset. | |
3008 { | |
3009 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3010 MutableEntry entry(&trans, CREATE, PREFERENCES, pref_root_id, "bob"); | |
3011 entry.PutServerNonUniqueName("bob"); | |
3012 entry.PutId(ids_.FromNumber(20)); | |
3013 entry.PutBaseVersion(1); | |
3014 entry.PutServerVersion(1); | |
3015 entry.PutIsUnsynced(true); | |
3016 entry.PutIsUnappliedUpdate(true); | |
3017 entry.PutSpecifics(DefaultPreferencesSpecifics()); | |
3018 entry.PutServerSpecifics(DefaultPreferencesSpecifics()); | |
3019 entry.PutIsDel(false); | |
3020 } | |
3021 | |
3022 EXPECT_TRUE(SyncShareNudge()); | |
3023 // Since the hierarchy isn't really changed (the type has flat hierarchy) | |
3024 // this conflict must be discarded. | |
3025 EXPECT_EQ(0, session_->status_controller().num_local_overwrites()); | |
3026 EXPECT_EQ(0, session_->status_controller().num_server_overwrites()); | |
3027 } | |
3028 | |
3029 TEST_F(SyncerTest, DeletingEntryWithLocalEdits) { | |
3030 int64_t newfolder_metahandle; | |
3031 | |
3032 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10, | |
3033 foreign_cache_guid(), "-1"); | |
3034 EXPECT_TRUE(SyncShareNudge()); | |
3035 { | |
3036 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3037 MutableEntry newfolder( | |
3038 &trans, CREATE, BOOKMARKS, ids_.FromNumber(1), "local"); | |
3039 ASSERT_TRUE(newfolder.good()); | |
3040 newfolder.PutIsUnsynced(true); | |
3041 newfolder.PutIsDir(true); | |
3042 newfolder.PutSpecifics(DefaultBookmarkSpecifics()); | |
3043 newfolder_metahandle = newfolder.GetMetahandle(); | |
3044 } | |
3045 mock_server_->AddUpdateDirectory(1, 0, "bob", 2, 20, | |
3046 foreign_cache_guid(), "-1"); | |
3047 mock_server_->SetLastUpdateDeleted(); | |
3048 SyncShareConfigure(); | |
3049 { | |
3050 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3051 Entry entry(&trans, syncable::GET_BY_HANDLE, newfolder_metahandle); | |
3052 ASSERT_TRUE(entry.good()); | |
3053 } | |
3054 } | |
3055 | |
3056 TEST_F(SyncerTest, FolderSwapUpdate) { | |
3057 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10, | |
3058 foreign_cache_guid(), "-7801"); | |
3059 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10, | |
3060 foreign_cache_guid(), "-1024"); | |
3061 EXPECT_TRUE(SyncShareNudge()); | |
3062 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20, | |
3063 foreign_cache_guid(), "-1024"); | |
3064 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20, | |
3065 foreign_cache_guid(), "-7801"); | |
3066 EXPECT_TRUE(SyncShareNudge()); | |
3067 { | |
3068 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3069 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801)); | |
3070 ASSERT_TRUE(id1.good()); | |
3071 EXPECT_EQ("fred", id1.GetNonUniqueName()); | |
3072 EXPECT_EQ(root_id_, id1.GetParentId()); | |
3073 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024)); | |
3074 ASSERT_TRUE(id2.good()); | |
3075 EXPECT_EQ("bob", id2.GetNonUniqueName()); | |
3076 EXPECT_EQ(root_id_, id2.GetParentId()); | |
3077 } | |
3078 } | |
3079 | |
3080 TEST_F(SyncerTest, NameCollidingFolderSwapWorksFine) { | |
3081 mock_server_->AddUpdateDirectory(7801, 0, "bob", 1, 10, | |
3082 foreign_cache_guid(), "-7801"); | |
3083 mock_server_->AddUpdateDirectory(1024, 0, "fred", 1, 10, | |
3084 foreign_cache_guid(), "-1024"); | |
3085 mock_server_->AddUpdateDirectory(4096, 0, "alice", 1, 10, | |
3086 foreign_cache_guid(), "-4096"); | |
3087 EXPECT_TRUE(SyncShareNudge()); | |
3088 { | |
3089 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3090 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801)); | |
3091 ASSERT_TRUE(id1.good()); | |
3092 EXPECT_EQ("bob", id1.GetNonUniqueName()); | |
3093 EXPECT_EQ(root_id_, id1.GetParentId()); | |
3094 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024)); | |
3095 ASSERT_TRUE(id2.good()); | |
3096 EXPECT_EQ("fred", id2.GetNonUniqueName()); | |
3097 EXPECT_EQ(root_id_, id2.GetParentId()); | |
3098 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096)); | |
3099 ASSERT_TRUE(id3.good()); | |
3100 EXPECT_EQ("alice", id3.GetNonUniqueName()); | |
3101 EXPECT_EQ(root_id_, id3.GetParentId()); | |
3102 } | |
3103 mock_server_->AddUpdateDirectory(1024, 0, "bob", 2, 20, | |
3104 foreign_cache_guid(), "-1024"); | |
3105 mock_server_->AddUpdateDirectory(7801, 0, "fred", 2, 20, | |
3106 foreign_cache_guid(), "-7801"); | |
3107 mock_server_->AddUpdateDirectory(4096, 0, "bob", 2, 20, | |
3108 foreign_cache_guid(), "-4096"); | |
3109 EXPECT_TRUE(SyncShareNudge()); | |
3110 { | |
3111 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3112 Entry id1(&trans, GET_BY_ID, ids_.FromNumber(7801)); | |
3113 ASSERT_TRUE(id1.good()); | |
3114 EXPECT_EQ("fred", id1.GetNonUniqueName()); | |
3115 EXPECT_EQ(root_id_, id1.GetParentId()); | |
3116 Entry id2(&trans, GET_BY_ID, ids_.FromNumber(1024)); | |
3117 ASSERT_TRUE(id2.good()); | |
3118 EXPECT_EQ("bob", id2.GetNonUniqueName()); | |
3119 EXPECT_EQ(root_id_, id2.GetParentId()); | |
3120 Entry id3(&trans, GET_BY_ID, ids_.FromNumber(4096)); | |
3121 ASSERT_TRUE(id3.good()); | |
3122 EXPECT_EQ("bob", id3.GetNonUniqueName()); | |
3123 EXPECT_EQ(root_id_, id3.GetParentId()); | |
3124 } | |
3125 } | |
3126 | |
3127 // Committing more than kDefaultMaxCommitBatchSize items requires that | |
3128 // we post more than one commit command to the server. This test makes | |
3129 // sure that scenario works as expected. | |
3130 TEST_F(SyncerTest, CommitManyItemsInOneGo_Success) { | |
3131 uint32_t num_batches = 3; | |
3132 uint32_t items_to_commit = kDefaultMaxCommitBatchSize * num_batches; | |
3133 { | |
3134 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3135 for (uint32_t i = 0; i < items_to_commit; i++) { | |
3136 string nameutf8 = base::UintToString(i); | |
3137 string name(nameutf8.begin(), nameutf8.end()); | |
3138 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name); | |
3139 e.PutIsUnsynced(true); | |
3140 e.PutIsDir(true); | |
3141 e.PutSpecifics(DefaultBookmarkSpecifics()); | |
3142 } | |
3143 } | |
3144 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count()); | |
3145 | |
3146 EXPECT_TRUE(SyncShareNudge()); | |
3147 EXPECT_EQ(num_batches, mock_server_->commit_messages().size()); | |
3148 EXPECT_EQ(0, directory()->unsynced_entity_count()); | |
3149 } | |
3150 | |
3151 // Test that a single failure to contact the server will cause us to exit the | |
3152 // commit loop immediately. | |
3153 TEST_F(SyncerTest, CommitManyItemsInOneGo_PostBufferFail) { | |
3154 uint32_t num_batches = 3; | |
3155 uint32_t items_to_commit = kDefaultMaxCommitBatchSize * num_batches; | |
3156 { | |
3157 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3158 for (uint32_t i = 0; i < items_to_commit; i++) { | |
3159 string nameutf8 = base::UintToString(i); | |
3160 string name(nameutf8.begin(), nameutf8.end()); | |
3161 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name); | |
3162 e.PutIsUnsynced(true); | |
3163 e.PutIsDir(true); | |
3164 e.PutSpecifics(DefaultBookmarkSpecifics()); | |
3165 } | |
3166 } | |
3167 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count()); | |
3168 | |
3169 // The second commit should fail. It will be preceded by one successful | |
3170 // GetUpdate and one succesful commit. | |
3171 mock_server_->FailNthPostBufferToPathCall(3); | |
3172 EXPECT_FALSE(SyncShareNudge()); | |
3173 | |
3174 EXPECT_EQ(1U, mock_server_->commit_messages().size()); | |
3175 EXPECT_EQ(SYNC_SERVER_ERROR, | |
3176 session_->status_controller().model_neutral_state().commit_result); | |
3177 EXPECT_EQ(items_to_commit - kDefaultMaxCommitBatchSize, | |
3178 directory()->unsynced_entity_count()); | |
3179 } | |
3180 | |
3181 // Test that a single conflict response from the server will cause us to exit | |
3182 // the commit loop immediately. | |
3183 TEST_F(SyncerTest, CommitManyItemsInOneGo_CommitConflict) { | |
3184 uint32_t num_batches = 2; | |
3185 uint32_t items_to_commit = kDefaultMaxCommitBatchSize * num_batches; | |
3186 { | |
3187 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3188 for (uint32_t i = 0; i < items_to_commit; i++) { | |
3189 string nameutf8 = base::UintToString(i); | |
3190 string name(nameutf8.begin(), nameutf8.end()); | |
3191 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), name); | |
3192 e.PutIsUnsynced(true); | |
3193 e.PutIsDir(true); | |
3194 e.PutSpecifics(DefaultBookmarkSpecifics()); | |
3195 } | |
3196 } | |
3197 ASSERT_EQ(items_to_commit, directory()->unsynced_entity_count()); | |
3198 | |
3199 // Return a CONFLICT response for the first item. | |
3200 mock_server_->set_conflict_n_commits(1); | |
3201 EXPECT_FALSE(SyncShareNudge()); | |
3202 | |
3203 // We should stop looping at the first sign of trouble. | |
3204 EXPECT_EQ(1U, mock_server_->commit_messages().size()); | |
3205 EXPECT_EQ(items_to_commit - (kDefaultMaxCommitBatchSize - 1), | |
3206 directory()->unsynced_entity_count()); | |
3207 } | |
3208 | |
3209 // Tests that sending debug info events works. | |
3210 TEST_F(SyncerTest, SendDebugInfoEventsOnGetUpdates_HappyCase) { | |
3211 debug_info_getter_->AddDebugEvent(); | |
3212 debug_info_getter_->AddDebugEvent(); | |
3213 | |
3214 EXPECT_TRUE(SyncShareNudge()); | |
3215 | |
3216 // Verify we received one GetUpdates request with two debug info events. | |
3217 EXPECT_EQ(1U, mock_server_->requests().size()); | |
3218 ASSERT_TRUE(mock_server_->last_request().has_get_updates()); | |
3219 EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size()); | |
3220 | |
3221 EXPECT_TRUE(SyncShareNudge()); | |
3222 | |
3223 // See that we received another GetUpdates request, but that it contains no | |
3224 // debug info events. | |
3225 EXPECT_EQ(2U, mock_server_->requests().size()); | |
3226 ASSERT_TRUE(mock_server_->last_request().has_get_updates()); | |
3227 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size()); | |
3228 | |
3229 debug_info_getter_->AddDebugEvent(); | |
3230 | |
3231 EXPECT_TRUE(SyncShareNudge()); | |
3232 | |
3233 // See that we received another GetUpdates request and it contains one debug | |
3234 // info event. | |
3235 EXPECT_EQ(3U, mock_server_->requests().size()); | |
3236 ASSERT_TRUE(mock_server_->last_request().has_get_updates()); | |
3237 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size()); | |
3238 } | |
3239 | |
3240 // Tests that debug info events are dropped on server error. | |
3241 TEST_F(SyncerTest, SendDebugInfoEventsOnGetUpdates_PostFailsDontDrop) { | |
3242 debug_info_getter_->AddDebugEvent(); | |
3243 debug_info_getter_->AddDebugEvent(); | |
3244 | |
3245 mock_server_->FailNextPostBufferToPathCall(); | |
3246 EXPECT_FALSE(SyncShareNudge()); | |
3247 | |
3248 // Verify we attempted to send one GetUpdates request with two debug info | |
3249 // events. | |
3250 EXPECT_EQ(1U, mock_server_->requests().size()); | |
3251 ASSERT_TRUE(mock_server_->last_request().has_get_updates()); | |
3252 EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size()); | |
3253 | |
3254 EXPECT_TRUE(SyncShareNudge()); | |
3255 | |
3256 // See that the client resent the two debug info events. | |
3257 EXPECT_EQ(2U, mock_server_->requests().size()); | |
3258 ASSERT_TRUE(mock_server_->last_request().has_get_updates()); | |
3259 EXPECT_EQ(2, mock_server_->last_request().debug_info().events_size()); | |
3260 | |
3261 // The previous send was successful so this next one shouldn't generate any | |
3262 // debug info events. | |
3263 EXPECT_TRUE(SyncShareNudge()); | |
3264 EXPECT_EQ(3U, mock_server_->requests().size()); | |
3265 ASSERT_TRUE(mock_server_->last_request().has_get_updates()); | |
3266 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size()); | |
3267 } | |
3268 | |
3269 // Tests that commit failure with conflict will trigger GetUpdates for next | |
3270 // cycle of sync | |
3271 TEST_F(SyncerTest, CommitFailureWithConflict) { | |
3272 ConfigureNoGetUpdatesRequired(); | |
3273 CreateUnsyncedDirectory("X", "id_X"); | |
3274 EXPECT_FALSE(nudge_tracker_.IsGetUpdatesRequired()); | |
3275 | |
3276 EXPECT_TRUE(SyncShareNudge()); | |
3277 EXPECT_FALSE(nudge_tracker_.IsGetUpdatesRequired()); | |
3278 | |
3279 CreateUnsyncedDirectory("Y", "id_Y"); | |
3280 mock_server_->set_conflict_n_commits(1); | |
3281 EXPECT_FALSE(SyncShareNudge()); | |
3282 EXPECT_TRUE(nudge_tracker_.IsGetUpdatesRequired()); | |
3283 | |
3284 nudge_tracker_.RecordSuccessfulSyncCycle(); | |
3285 EXPECT_FALSE(nudge_tracker_.IsGetUpdatesRequired()); | |
3286 } | |
3287 | |
3288 // Tests that sending debug info events on Commit works. | |
3289 TEST_F(SyncerTest, SendDebugInfoEventsOnCommit_HappyCase) { | |
3290 // Make sure GetUpdate isn't call as it would "steal" debug info events before | |
3291 // Commit has a chance to send them. | |
3292 ConfigureNoGetUpdatesRequired(); | |
3293 | |
3294 // Generate a debug info event and trigger a commit. | |
3295 debug_info_getter_->AddDebugEvent(); | |
3296 CreateUnsyncedDirectory("X", "id_X"); | |
3297 EXPECT_TRUE(SyncShareNudge()); | |
3298 | |
3299 // Verify that the last request received is a Commit and that it contains a | |
3300 // debug info event. | |
3301 EXPECT_EQ(1U, mock_server_->requests().size()); | |
3302 ASSERT_TRUE(mock_server_->last_request().has_commit()); | |
3303 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size()); | |
3304 | |
3305 // Generate another commit, but no debug info event. | |
3306 CreateUnsyncedDirectory("Y", "id_Y"); | |
3307 EXPECT_TRUE(SyncShareNudge()); | |
3308 | |
3309 // See that it was received and contains no debug info events. | |
3310 EXPECT_EQ(2U, mock_server_->requests().size()); | |
3311 ASSERT_TRUE(mock_server_->last_request().has_commit()); | |
3312 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size()); | |
3313 } | |
3314 | |
3315 // Tests that debug info events are not dropped on server error. | |
3316 TEST_F(SyncerTest, SendDebugInfoEventsOnCommit_PostFailsDontDrop) { | |
3317 // Make sure GetUpdate isn't call as it would "steal" debug info events before | |
3318 // Commit has a chance to send them. | |
3319 ConfigureNoGetUpdatesRequired(); | |
3320 | |
3321 mock_server_->FailNextPostBufferToPathCall(); | |
3322 | |
3323 // Generate a debug info event and trigger a commit. | |
3324 debug_info_getter_->AddDebugEvent(); | |
3325 CreateUnsyncedDirectory("X", "id_X"); | |
3326 EXPECT_FALSE(SyncShareNudge()); | |
3327 | |
3328 // Verify that the last request sent is a Commit and that it contains a debug | |
3329 // info event. | |
3330 EXPECT_EQ(1U, mock_server_->requests().size()); | |
3331 ASSERT_TRUE(mock_server_->last_request().has_commit()); | |
3332 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size()); | |
3333 | |
3334 // Try again. | |
3335 EXPECT_TRUE(SyncShareNudge()); | |
3336 | |
3337 // Verify that we've received another Commit and that it contains a debug info | |
3338 // event (just like the previous one). | |
3339 EXPECT_EQ(2U, mock_server_->requests().size()); | |
3340 ASSERT_TRUE(mock_server_->last_request().has_commit()); | |
3341 EXPECT_EQ(1, mock_server_->last_request().debug_info().events_size()); | |
3342 | |
3343 // Generate another commit and try again. | |
3344 CreateUnsyncedDirectory("Y", "id_Y"); | |
3345 EXPECT_TRUE(SyncShareNudge()); | |
3346 | |
3347 // See that it was received and contains no debug info events. | |
3348 EXPECT_EQ(3U, mock_server_->requests().size()); | |
3349 ASSERT_TRUE(mock_server_->last_request().has_commit()); | |
3350 EXPECT_EQ(0, mock_server_->last_request().debug_info().events_size()); | |
3351 } | |
3352 | |
3353 TEST_F(SyncerTest, HugeConflict) { | |
3354 int item_count = 300; // We should be able to do 300 or 3000 w/o issue. | |
3355 | |
3356 syncable::Id parent_id = ids_.NewServerId(); | |
3357 syncable::Id last_id = parent_id; | |
3358 vector<syncable::Id> tree_ids; | |
3359 | |
3360 // Create a lot of updates for which the parent does not exist yet. | |
3361 // Generate a huge deep tree which should all fail to apply at first. | |
3362 { | |
3363 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3364 for (int i = 0; i < item_count ; i++) { | |
3365 syncable::Id next_id = ids_.NewServerId(); | |
3366 syncable::Id local_id = ids_.NewLocalId(); | |
3367 tree_ids.push_back(next_id); | |
3368 mock_server_->AddUpdateDirectory(next_id, last_id, "BOB", 2, 20, | |
3369 foreign_cache_guid(), | |
3370 local_id.GetServerId()); | |
3371 last_id = next_id; | |
3372 } | |
3373 } | |
3374 EXPECT_TRUE(SyncShareNudge()); | |
3375 | |
3376 // Check they're in the expected conflict state. | |
3377 { | |
3378 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3379 for (int i = 0; i < item_count; i++) { | |
3380 Entry e(&trans, GET_BY_ID, tree_ids[i]); | |
3381 // They should all exist but none should be applied. | |
3382 ASSERT_TRUE(e.good()); | |
3383 EXPECT_TRUE(e.GetIsDel()); | |
3384 EXPECT_TRUE(e.GetIsUnappliedUpdate()); | |
3385 } | |
3386 } | |
3387 | |
3388 // Add the missing parent directory. | |
3389 mock_server_->AddUpdateDirectory(parent_id, TestIdFactory::root(), | |
3390 "BOB", 2, 20, foreign_cache_guid(), "-3500"); | |
3391 EXPECT_TRUE(SyncShareNudge()); | |
3392 | |
3393 // Now they should all be OK. | |
3394 { | |
3395 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3396 for (int i = 0; i < item_count; i++) { | |
3397 Entry e(&trans, GET_BY_ID, tree_ids[i]); | |
3398 ASSERT_TRUE(e.good()); | |
3399 EXPECT_FALSE(e.GetIsDel()); | |
3400 EXPECT_FALSE(e.GetIsUnappliedUpdate()); | |
3401 } | |
3402 } | |
3403 } | |
3404 | |
3405 TEST_F(SyncerTest, DontCrashOnCaseChange) { | |
3406 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10, | |
3407 foreign_cache_guid(), "-1"); | |
3408 EXPECT_TRUE(SyncShareNudge()); | |
3409 { | |
3410 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3411 MutableEntry e(&trans, GET_BY_ID, ids_.FromNumber(1)); | |
3412 ASSERT_TRUE(e.good()); | |
3413 e.PutIsUnsynced(true); | |
3414 } | |
3415 mock_server_->set_conflict_all_commits(true); | |
3416 mock_server_->AddUpdateDirectory(1, 0, "BOB", 2, 20, | |
3417 foreign_cache_guid(), "-1"); | |
3418 EXPECT_FALSE(SyncShareNudge()); // USED TO CAUSE AN ASSERT | |
3419 } | |
3420 | |
3421 TEST_F(SyncerTest, UnsyncedItemAndUpdate) { | |
3422 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10, | |
3423 foreign_cache_guid(), "-1"); | |
3424 EXPECT_TRUE(SyncShareNudge()); | |
3425 mock_server_->set_conflict_all_commits(true); | |
3426 mock_server_->AddUpdateDirectory(2, 0, "bob", 2, 20, | |
3427 foreign_cache_guid(), "-2"); | |
3428 EXPECT_TRUE(SyncShareNudge()); // USED TO CAUSE AN ASSERT | |
3429 } | |
3430 | |
3431 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath) { | |
3432 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10, | |
3433 foreign_cache_guid(), "-1"); | |
3434 EXPECT_TRUE(SyncShareNudge()); | |
3435 int64_t local_folder_handle; | |
3436 syncable::Id local_folder_id; | |
3437 { | |
3438 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
3439 MutableEntry new_entry( | |
3440 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm"); | |
3441 ASSERT_TRUE(new_entry.good()); | |
3442 local_folder_id = new_entry.GetId(); | |
3443 local_folder_handle = new_entry.GetMetahandle(); | |
3444 new_entry.PutIsUnsynced(true); | |
3445 new_entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
3446 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
3447 ASSERT_TRUE(old.good()); | |
3448 WriteTestDataToEntry(&wtrans, &old); | |
3449 } | |
3450 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20, | |
3451 foreign_cache_guid(), "-1"); | |
3452 mock_server_->set_conflict_all_commits(true); | |
3453 EXPECT_FALSE(SyncShareNudge()); | |
3454 { | |
3455 // Update #20 should have been dropped in favor of the local version. | |
3456 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
3457 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
3458 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle); | |
3459 ASSERT_TRUE(server.good()); | |
3460 ASSERT_TRUE(local.good()); | |
3461 EXPECT_NE(local.GetMetahandle(), server.GetMetahandle()); | |
3462 EXPECT_FALSE(server.GetIsUnappliedUpdate()); | |
3463 EXPECT_FALSE(local.GetIsUnappliedUpdate()); | |
3464 EXPECT_TRUE(server.GetIsUnsynced()); | |
3465 EXPECT_TRUE(local.GetIsUnsynced()); | |
3466 EXPECT_EQ("Foo.htm", server.GetNonUniqueName()); | |
3467 EXPECT_EQ("Bar.htm", local.GetNonUniqueName()); | |
3468 } | |
3469 // Allow local changes to commit. | |
3470 mock_server_->set_conflict_all_commits(false); | |
3471 EXPECT_TRUE(SyncShareNudge()); | |
3472 | |
3473 // Now add a server change to make the two names equal. There should | |
3474 // be no conflict with that, since names are not unique. | |
3475 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30, | |
3476 foreign_cache_guid(), "-1"); | |
3477 EXPECT_TRUE(SyncShareNudge()); | |
3478 { | |
3479 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
3480 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
3481 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle); | |
3482 ASSERT_TRUE(server.good()); | |
3483 ASSERT_TRUE(local.good()); | |
3484 EXPECT_NE(local.GetMetahandle(), server.GetMetahandle()); | |
3485 EXPECT_FALSE(server.GetIsUnappliedUpdate()); | |
3486 EXPECT_FALSE(local.GetIsUnappliedUpdate()); | |
3487 EXPECT_FALSE(server.GetIsUnsynced()); | |
3488 EXPECT_FALSE(local.GetIsUnsynced()); | |
3489 EXPECT_EQ("Bar.htm", server.GetNonUniqueName()); | |
3490 EXPECT_EQ("Bar.htm", local.GetNonUniqueName()); | |
3491 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark. | |
3492 server.GetSpecifics().bookmark().url()); | |
3493 } | |
3494 } | |
3495 | |
3496 // Same as NewEntryAnddServerEntrySharePath, but using the old-style protocol. | |
3497 TEST_F(SyncerTest, NewEntryAndAlteredServerEntrySharePath_OldBookmarksProto) { | |
3498 mock_server_->set_use_legacy_bookmarks_protocol(true); | |
3499 mock_server_->AddUpdateBookmark(1, 0, "Foo.htm", 10, 10, | |
3500 foreign_cache_guid(), "-1"); | |
3501 EXPECT_TRUE(SyncShareNudge()); | |
3502 int64_t local_folder_handle; | |
3503 syncable::Id local_folder_id; | |
3504 { | |
3505 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
3506 MutableEntry new_entry( | |
3507 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "Bar.htm"); | |
3508 ASSERT_TRUE(new_entry.good()); | |
3509 local_folder_id = new_entry.GetId(); | |
3510 local_folder_handle = new_entry.GetMetahandle(); | |
3511 new_entry.PutIsUnsynced(true); | |
3512 new_entry.PutSpecifics(DefaultBookmarkSpecifics()); | |
3513 MutableEntry old(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
3514 ASSERT_TRUE(old.good()); | |
3515 WriteTestDataToEntry(&wtrans, &old); | |
3516 } | |
3517 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 20, 20, | |
3518 foreign_cache_guid(), "-1"); | |
3519 mock_server_->set_conflict_all_commits(true); | |
3520 EXPECT_FALSE(SyncShareNudge()); | |
3521 { | |
3522 // Update #20 should have been dropped in favor of the local version. | |
3523 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
3524 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
3525 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle); | |
3526 ASSERT_TRUE(server.good()); | |
3527 ASSERT_TRUE(local.good()); | |
3528 EXPECT_NE(local.GetMetahandle(), server.GetMetahandle()); | |
3529 EXPECT_FALSE(server.GetIsUnappliedUpdate()); | |
3530 EXPECT_FALSE(local.GetIsUnappliedUpdate()); | |
3531 EXPECT_TRUE(server.GetIsUnsynced()); | |
3532 EXPECT_TRUE(local.GetIsUnsynced()); | |
3533 EXPECT_EQ("Foo.htm", server.GetNonUniqueName()); | |
3534 EXPECT_EQ("Bar.htm", local.GetNonUniqueName()); | |
3535 } | |
3536 // Allow local changes to commit. | |
3537 mock_server_->set_conflict_all_commits(false); | |
3538 EXPECT_TRUE(SyncShareNudge()); | |
3539 | |
3540 // Now add a server change to make the two names equal. There should | |
3541 // be no conflict with that, since names are not unique. | |
3542 mock_server_->AddUpdateBookmark(1, 0, "Bar.htm", 30, 30, | |
3543 foreign_cache_guid(), "-1"); | |
3544 EXPECT_TRUE(SyncShareNudge()); | |
3545 { | |
3546 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
3547 MutableEntry server(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
3548 MutableEntry local(&wtrans, GET_BY_HANDLE, local_folder_handle); | |
3549 ASSERT_TRUE(server.good()); | |
3550 ASSERT_TRUE(local.good()); | |
3551 EXPECT_NE(local.GetMetahandle(), server.GetMetahandle()); | |
3552 EXPECT_FALSE(server.GetIsUnappliedUpdate()); | |
3553 EXPECT_FALSE(local.GetIsUnappliedUpdate()); | |
3554 EXPECT_FALSE(server.GetIsUnsynced()); | |
3555 EXPECT_FALSE(local.GetIsUnsynced()); | |
3556 EXPECT_EQ("Bar.htm", server.GetNonUniqueName()); | |
3557 EXPECT_EQ("Bar.htm", local.GetNonUniqueName()); | |
3558 EXPECT_EQ("http://google.com", // Default from AddUpdateBookmark. | |
3559 server.GetSpecifics().bookmark().url()); | |
3560 } | |
3561 } | |
3562 | |
3563 // Circular links should be resolved by the server. | |
3564 TEST_F(SyncerTest, SiblingDirectoriesBecomeCircular) { | |
3565 // we don't currently resolve this. This test ensures we don't. | |
3566 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10, | |
3567 foreign_cache_guid(), "-1"); | |
3568 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10, | |
3569 foreign_cache_guid(), "-2"); | |
3570 EXPECT_TRUE(SyncShareNudge()); | |
3571 { | |
3572 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
3573 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
3574 ASSERT_TRUE(A.good()); | |
3575 A.PutIsUnsynced(true); | |
3576 A.PutParentId(ids_.FromNumber(2)); | |
3577 A.PutNonUniqueName("B"); | |
3578 } | |
3579 mock_server_->AddUpdateDirectory(2, 1, "A", 20, 20, | |
3580 foreign_cache_guid(), "-2"); | |
3581 mock_server_->set_conflict_all_commits(true); | |
3582 EXPECT_FALSE(SyncShareNudge()); | |
3583 { | |
3584 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
3585 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
3586 ASSERT_TRUE(A.good()); | |
3587 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); | |
3588 ASSERT_TRUE(B.good()); | |
3589 EXPECT_EQ("B", A.GetNonUniqueName()); | |
3590 EXPECT_EQ("B", B.GetNonUniqueName()); | |
3591 } | |
3592 } | |
3593 | |
3594 TEST_F(SyncerTest, SwapEntryNames) { | |
3595 // Simple transaction test. | |
3596 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10, | |
3597 foreign_cache_guid(), "-1"); | |
3598 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10, | |
3599 foreign_cache_guid(), "-2"); | |
3600 mock_server_->set_conflict_all_commits(true); | |
3601 EXPECT_TRUE(SyncShareNudge()); | |
3602 { | |
3603 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
3604 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
3605 ASSERT_TRUE(A.good()); | |
3606 A.PutIsUnsynced(true); | |
3607 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); | |
3608 ASSERT_TRUE(B.good()); | |
3609 B.PutIsUnsynced(true); | |
3610 A.PutNonUniqueName("C"); | |
3611 B.PutNonUniqueName("A"); | |
3612 A.PutNonUniqueName("B"); | |
3613 } | |
3614 EXPECT_FALSE(SyncShareNudge()); | |
3615 } | |
3616 | |
3617 TEST_F(SyncerTest, DualDeletionWithNewItemNameClash) { | |
3618 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10, | |
3619 foreign_cache_guid(), "-1"); | |
3620 mock_server_->AddUpdateBookmark(2, 0, "B", 10, 10, | |
3621 foreign_cache_guid(), "-2"); | |
3622 mock_server_->set_conflict_all_commits(true); | |
3623 EXPECT_TRUE(SyncShareNudge()); | |
3624 { | |
3625 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3626 MutableEntry B(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
3627 ASSERT_TRUE(B.good()); | |
3628 WriteTestDataToEntry(&trans, &B); | |
3629 B.PutIsDel(true); | |
3630 } | |
3631 mock_server_->AddUpdateBookmark(2, 0, "A", 11, 11, | |
3632 foreign_cache_guid(), "-2"); | |
3633 mock_server_->SetLastUpdateDeleted(); | |
3634 EXPECT_TRUE(SyncShareNudge()); | |
3635 { | |
3636 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3637 Entry B(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
3638 ASSERT_TRUE(B.good()); | |
3639 EXPECT_FALSE(B.GetIsUnsynced()); | |
3640 EXPECT_FALSE(B.GetIsUnappliedUpdate()); | |
3641 } | |
3642 } | |
3643 | |
3644 // When we undelete an entity as a result of conflict resolution, we reuse the | |
3645 // existing server id and preserve the old version, simply updating the server | |
3646 // version with the new non-deleted entity. | |
3647 TEST_F(SyncerTest, ResolveWeWroteTheyDeleted) { | |
3648 int64_t bob_metahandle; | |
3649 | |
3650 mock_server_->AddUpdateBookmark(1, 0, "bob", 1, 10, | |
3651 foreign_cache_guid(), "-1"); | |
3652 EXPECT_TRUE(SyncShareNudge()); | |
3653 { | |
3654 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3655 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1)); | |
3656 ASSERT_TRUE(bob.good()); | |
3657 bob_metahandle = bob.GetMetahandle(); | |
3658 WriteTestDataToEntry(&trans, &bob); | |
3659 } | |
3660 mock_server_->AddUpdateBookmark(1, 0, "bob", 2, 10, | |
3661 foreign_cache_guid(), "-1"); | |
3662 mock_server_->SetLastUpdateDeleted(); | |
3663 mock_server_->set_conflict_all_commits(true); | |
3664 EXPECT_FALSE(SyncShareNudge()); | |
3665 EXPECT_FALSE(SyncShareNudge()); | |
3666 { | |
3667 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3668 Entry bob(&trans, GET_BY_HANDLE, bob_metahandle); | |
3669 ASSERT_TRUE(bob.good()); | |
3670 EXPECT_TRUE(bob.GetIsUnsynced()); | |
3671 EXPECT_TRUE(bob.GetId().ServerKnows()); | |
3672 EXPECT_FALSE(bob.GetIsUnappliedUpdate()); | |
3673 EXPECT_FALSE(bob.GetIsDel()); | |
3674 EXPECT_EQ(2, bob.GetServerVersion()); | |
3675 EXPECT_EQ(2, bob.GetBaseVersion()); | |
3676 } | |
3677 } | |
3678 | |
3679 // This test is to reproduce a check failure. Sometimes we would get a bad ID | |
3680 // back when creating an entry. | |
3681 TEST_F(SyncerTest, DuplicateIDReturn) { | |
3682 { | |
3683 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3684 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "bob"); | |
3685 ASSERT_TRUE(folder.good()); | |
3686 folder.PutIsUnsynced(true); | |
3687 folder.PutIsDir(true); | |
3688 folder.PutSpecifics(DefaultBookmarkSpecifics()); | |
3689 MutableEntry folder2(&trans, CREATE, BOOKMARKS, trans.root_id(), "fred"); | |
3690 ASSERT_TRUE(folder2.good()); | |
3691 folder2.PutIsUnsynced(false); | |
3692 folder2.PutIsDir(true); | |
3693 folder2.PutSpecifics(DefaultBookmarkSpecifics()); | |
3694 folder2.PutBaseVersion(3); | |
3695 folder2.PutId(syncable::Id::CreateFromServerId("mock_server:10000")); | |
3696 } | |
3697 mock_server_->set_next_new_id(10000); | |
3698 EXPECT_EQ(1u, directory()->unsynced_entity_count()); | |
3699 // we get back a bad id in here (should never happen). | |
3700 EXPECT_FALSE(SyncShareNudge()); | |
3701 EXPECT_EQ(1u, directory()->unsynced_entity_count()); | |
3702 EXPECT_TRUE(SyncShareNudge()); // another bad id in here. | |
3703 EXPECT_EQ(0u, directory()->unsynced_entity_count()); | |
3704 } | |
3705 | |
3706 TEST_F(SyncerTest, DeletedEntryWithBadParentInLoopCalculation) { | |
3707 mock_server_->AddUpdateDirectory(1, 0, "bob", 1, 10, | |
3708 foreign_cache_guid(), "-1"); | |
3709 EXPECT_TRUE(SyncShareNudge()); | |
3710 { | |
3711 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3712 MutableEntry bob(&trans, GET_BY_ID, ids_.FromNumber(1)); | |
3713 ASSERT_TRUE(bob.good()); | |
3714 // This is valid, because the parent could have gone away a long time ago. | |
3715 bob.PutParentId(ids_.FromNumber(54)); | |
3716 bob.PutIsDel(true); | |
3717 bob.PutIsUnsynced(true); | |
3718 } | |
3719 mock_server_->AddUpdateDirectory(2, 1, "fred", 1, 10, | |
3720 foreign_cache_guid(), "-2"); | |
3721 EXPECT_TRUE(SyncShareNudge()); | |
3722 EXPECT_TRUE(SyncShareNudge()); | |
3723 } | |
3724 | |
3725 TEST_F(SyncerTest, ConflictResolverMergesLocalDeleteAndServerUpdate) { | |
3726 syncable::Id local_id; | |
3727 { | |
3728 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3729 | |
3730 MutableEntry local_deleted( | |
3731 &trans, CREATE, BOOKMARKS, trans.root_id(), "name"); | |
3732 local_id = local_deleted.GetId(); | |
3733 local_deleted.PutId(ids_.FromNumber(1)); | |
3734 local_deleted.PutBaseVersion(1); | |
3735 local_deleted.PutIsDel(true); | |
3736 local_deleted.PutIsDir(false); | |
3737 local_deleted.PutIsUnsynced(true); | |
3738 local_deleted.PutSpecifics(DefaultBookmarkSpecifics()); | |
3739 } | |
3740 | |
3741 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10, | |
3742 local_cache_guid(), | |
3743 local_id.GetServerId()); | |
3744 | |
3745 // We don't care about actually committing, just the resolution. | |
3746 mock_server_->set_conflict_all_commits(true); | |
3747 EXPECT_FALSE(SyncShareNudge()); | |
3748 | |
3749 { | |
3750 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3751 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1)); | |
3752 EXPECT_EQ(10, local_deleted.GetBaseVersion()); | |
3753 EXPECT_FALSE(local_deleted.GetIsUnappliedUpdate()); | |
3754 EXPECT_TRUE(local_deleted.GetIsUnsynced()); | |
3755 EXPECT_TRUE(local_deleted.GetIsDel()); | |
3756 EXPECT_FALSE(local_deleted.GetIsDir()); | |
3757 } | |
3758 } | |
3759 | |
3760 // This ensures that for extensions, we resolve the conflict of local updates | |
3761 // and server deletes in favor of the server, to prevent extensions from | |
3762 // being reinstalled after uninstall. | |
3763 TEST_F(SyncerTest, ConflictResolverAcceptsServerDeleteForExtensions) { | |
3764 ASSERT_TRUE(context_->GetEnabledTypes().Has(EXTENSIONS)); | |
3765 | |
3766 // Create an extension entry. | |
3767 int64_t metahandle; | |
3768 { | |
3769 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3770 MutableEntry extension( | |
3771 &trans, CREATE, EXTENSIONS, trans.root_id(), "extension_name"); | |
3772 ASSERT_TRUE(extension.good()); | |
3773 sync_pb::EntitySpecifics specifics; | |
3774 AddDefaultFieldValue(EXTENSIONS, &specifics); | |
3775 extension.PutSpecifics(specifics); | |
3776 EXPECT_FALSE(extension.GetIsUnappliedUpdate()); | |
3777 EXPECT_FALSE(extension.GetId().ServerKnows()); | |
3778 metahandle = extension.GetMetahandle(); | |
3779 extension.PutIsUnsynced(true); | |
3780 } | |
3781 | |
3782 // Make sure the server has received the new item. | |
3783 SyncShareNudge(); | |
3784 syncable::Id id; | |
3785 { | |
3786 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3787 Entry entry(&trans, GET_BY_HANDLE, metahandle); | |
3788 | |
3789 EXPECT_EQ(metahandle, entry.GetMetahandle()); | |
3790 EXPECT_FALSE(entry.GetIsDel()); | |
3791 EXPECT_FALSE(entry.GetServerIsDel()); | |
3792 EXPECT_GE(entry.GetBaseVersion(), 0); | |
3793 EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion()); | |
3794 EXPECT_FALSE(entry.GetIsUnsynced()); | |
3795 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
3796 id = entry.GetId(); | |
3797 } | |
3798 | |
3799 | |
3800 // Simulate another client deleting the item. | |
3801 { | |
3802 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3803 Entry entry(&trans, GET_BY_HANDLE, metahandle); | |
3804 mock_server_->AddUpdateTombstone(id, EXTENSIONS); | |
3805 } | |
3806 | |
3807 // Create a local update, which should cause a conflict with the delete that | |
3808 // we just pushed to the server. | |
3809 { | |
3810 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3811 MutableEntry extension(&trans, GET_BY_HANDLE, metahandle); | |
3812 ASSERT_TRUE(extension.good()); | |
3813 sync_pb::EntitySpecifics specifics; | |
3814 AddDefaultFieldValue(EXTENSIONS, &specifics); | |
3815 specifics.mutable_extension()->set_disable_reasons(2); | |
3816 extension.PutSpecifics(specifics); | |
3817 EXPECT_FALSE(extension.GetIsUnappliedUpdate()); | |
3818 extension.PutIsUnsynced(true); | |
3819 } | |
3820 | |
3821 // Run a sync, and expect the item to be deleted. | |
3822 SyncShareNudge(); | |
3823 { | |
3824 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3825 Entry entry(&trans, GET_BY_HANDLE, metahandle); | |
3826 EXPECT_EQ(metahandle, entry.GetMetahandle()); | |
3827 EXPECT_TRUE(entry.GetIsDel()); | |
3828 EXPECT_TRUE(entry.GetServerIsDel()); | |
3829 EXPECT_FALSE(entry.GetIsUnsynced()); | |
3830 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
3831 EXPECT_GE(entry.GetBaseVersion(), 0); | |
3832 EXPECT_GE(entry.GetServerVersion(), 0); | |
3833 } | |
3834 } | |
3835 | |
3836 // See what happens if the IS_DIR bit gets flipped. This can cause us | |
3837 // all kinds of disasters. | |
3838 TEST_F(SyncerTest, UpdateFlipsTheFolderBit) { | |
3839 // Local object: a deleted directory (container), revision 1, unsynced. | |
3840 { | |
3841 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3842 | |
3843 MutableEntry local_deleted( | |
3844 &trans, CREATE, BOOKMARKS, trans.root_id(), "name"); | |
3845 local_deleted.PutId(ids_.FromNumber(1)); | |
3846 local_deleted.PutBaseVersion(1); | |
3847 local_deleted.PutIsDel(true); | |
3848 local_deleted.PutIsDir(true); | |
3849 local_deleted.PutIsUnsynced(true); | |
3850 local_deleted.PutSpecifics(DefaultBookmarkSpecifics()); | |
3851 } | |
3852 | |
3853 // Server update: entry-type object (not a container), revision 10. | |
3854 mock_server_->AddUpdateBookmark(ids_.FromNumber(1), root_id_, "name", 10, 10, | |
3855 local_cache_guid(), | |
3856 ids_.FromNumber(1).GetServerId()); | |
3857 | |
3858 // Don't attempt to commit. | |
3859 mock_server_->set_conflict_all_commits(true); | |
3860 | |
3861 // The syncer should not attempt to apply the invalid update. | |
3862 EXPECT_FALSE(SyncShareNudge()); | |
3863 | |
3864 { | |
3865 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3866 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1)); | |
3867 EXPECT_EQ(1, local_deleted.GetBaseVersion()); | |
3868 EXPECT_FALSE(local_deleted.GetIsUnappliedUpdate()); | |
3869 EXPECT_TRUE(local_deleted.GetIsUnsynced()); | |
3870 EXPECT_TRUE(local_deleted.GetIsDel()); | |
3871 EXPECT_TRUE(local_deleted.GetIsDir()); | |
3872 } | |
3873 } | |
3874 | |
3875 // Bug Synopsis: | |
3876 // Merge conflict resolution will merge a new local entry with another entry | |
3877 // that needs updates, resulting in CHECK. | |
3878 TEST_F(SyncerTest, MergingExistingItems) { | |
3879 mock_server_->set_conflict_all_commits(true); | |
3880 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10, | |
3881 local_cache_guid(), "-1"); | |
3882 EXPECT_TRUE(SyncShareNudge()); | |
3883 { | |
3884 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3885 MutableEntry entry( | |
3886 &trans, CREATE, BOOKMARKS, trans.root_id(), "Copy of base"); | |
3887 WriteTestDataToEntry(&trans, &entry); | |
3888 } | |
3889 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50, | |
3890 local_cache_guid(), "-1"); | |
3891 EXPECT_FALSE(SyncShareNudge()); | |
3892 } | |
3893 | |
3894 // In this test a long changelog contains a child at the start of the changelog | |
3895 // and a parent at the end. While these updates are in progress the client would | |
3896 // appear stuck. | |
3897 TEST_F(SyncerTest, LongChangelistWithApplicationConflict) { | |
3898 const int depth = 400; | |
3899 syncable::Id folder_id = ids_.FromNumber(1); | |
3900 | |
3901 // First we an item in a folder in the root. However the folder won't come | |
3902 // till much later. | |
3903 syncable::Id stuck_entry_id = TestIdFactory::FromNumber(99999); | |
3904 mock_server_->AddUpdateDirectory(stuck_entry_id, | |
3905 folder_id, "stuck", 1, 1, | |
3906 foreign_cache_guid(), "-99999"); | |
3907 mock_server_->SetChangesRemaining(depth - 1); | |
3908 EXPECT_TRUE(SyncShareNudge()); | |
3909 | |
3910 // Buffer up a very long series of downloads. | |
3911 // We should never be stuck (conflict resolution shouldn't | |
3912 // kick in so long as we're making forward progress). | |
3913 for (int i = 0; i < depth; i++) { | |
3914 mock_server_->NextUpdateBatch(); | |
3915 mock_server_->SetNewTimestamp(i + 1); | |
3916 mock_server_->SetChangesRemaining(depth - i); | |
3917 } | |
3918 | |
3919 EXPECT_TRUE(SyncShareNudge()); | |
3920 | |
3921 // Ensure our folder hasn't somehow applied. | |
3922 { | |
3923 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3924 Entry child(&trans, GET_BY_ID, stuck_entry_id); | |
3925 EXPECT_TRUE(child.good()); | |
3926 EXPECT_TRUE(child.GetIsUnappliedUpdate()); | |
3927 EXPECT_TRUE(child.GetIsDel()); | |
3928 EXPECT_FALSE(child.GetIsUnsynced()); | |
3929 } | |
3930 | |
3931 // And finally the folder. | |
3932 mock_server_->AddUpdateDirectory(folder_id, | |
3933 TestIdFactory::root(), "folder", 1, 1, | |
3934 foreign_cache_guid(), "-1"); | |
3935 mock_server_->SetChangesRemaining(0); | |
3936 EXPECT_TRUE(SyncShareNudge()); | |
3937 EXPECT_TRUE(SyncShareNudge()); | |
3938 // Check that everything is as expected after the commit. | |
3939 { | |
3940 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3941 Entry entry(&trans, GET_BY_ID, folder_id); | |
3942 ASSERT_TRUE(entry.good()); | |
3943 Entry child(&trans, GET_BY_ID, stuck_entry_id); | |
3944 EXPECT_EQ(entry.GetId(), child.GetParentId()); | |
3945 EXPECT_EQ("stuck", child.GetNonUniqueName()); | |
3946 EXPECT_TRUE(child.good()); | |
3947 } | |
3948 } | |
3949 | |
3950 TEST_F(SyncerTest, DontMergeTwoExistingItems) { | |
3951 mock_server_->set_conflict_all_commits(true); | |
3952 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10, | |
3953 foreign_cache_guid(), "-1"); | |
3954 mock_server_->AddUpdateBookmark(2, 0, "base2", 10, 10, | |
3955 foreign_cache_guid(), "-2"); | |
3956 EXPECT_TRUE(SyncShareNudge()); | |
3957 { | |
3958 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
3959 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
3960 ASSERT_TRUE(entry.good()); | |
3961 entry.PutNonUniqueName("Copy of base"); | |
3962 entry.PutIsUnsynced(true); | |
3963 } | |
3964 mock_server_->AddUpdateBookmark(1, 0, "Copy of base", 50, 50, | |
3965 foreign_cache_guid(), "-1"); | |
3966 EXPECT_FALSE(SyncShareNudge()); | |
3967 { | |
3968 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3969 Entry entry1(&trans, GET_BY_ID, ids_.FromNumber(1)); | |
3970 EXPECT_FALSE(entry1.GetIsUnappliedUpdate()); | |
3971 EXPECT_FALSE(entry1.GetIsUnsynced()); | |
3972 EXPECT_FALSE(entry1.GetIsDel()); | |
3973 Entry entry2(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
3974 EXPECT_FALSE(entry2.GetIsUnappliedUpdate()); | |
3975 EXPECT_TRUE(entry2.GetIsUnsynced()); | |
3976 EXPECT_FALSE(entry2.GetIsDel()); | |
3977 EXPECT_EQ(entry1.GetNonUniqueName(), entry2.GetNonUniqueName()); | |
3978 } | |
3979 } | |
3980 | |
3981 TEST_F(SyncerTest, TestUndeleteUpdate) { | |
3982 mock_server_->set_conflict_all_commits(true); | |
3983 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1, | |
3984 foreign_cache_guid(), "-1"); | |
3985 mock_server_->AddUpdateDirectory(2, 1, "bar", 1, 2, | |
3986 foreign_cache_guid(), "-2"); | |
3987 EXPECT_TRUE(SyncShareNudge()); | |
3988 mock_server_->AddUpdateDirectory(2, 1, "bar", 2, 3, | |
3989 foreign_cache_guid(), "-2"); | |
3990 mock_server_->SetLastUpdateDeleted(); | |
3991 EXPECT_TRUE(SyncShareNudge()); | |
3992 | |
3993 int64_t metahandle; | |
3994 { | |
3995 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
3996 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
3997 ASSERT_TRUE(entry.good()); | |
3998 EXPECT_TRUE(entry.GetIsDel()); | |
3999 metahandle = entry.GetMetahandle(); | |
4000 } | |
4001 mock_server_->AddUpdateDirectory(1, 0, "foo", 2, 4, | |
4002 foreign_cache_guid(), "-1"); | |
4003 mock_server_->SetLastUpdateDeleted(); | |
4004 EXPECT_TRUE(SyncShareNudge()); | |
4005 // This used to be rejected as it's an undeletion. Now, it results in moving | |
4006 // the delete path aside. | |
4007 mock_server_->AddUpdateDirectory(2, 1, "bar", 3, 5, | |
4008 foreign_cache_guid(), "-2"); | |
4009 EXPECT_TRUE(SyncShareNudge()); | |
4010 { | |
4011 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4012 Entry entry(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
4013 ASSERT_TRUE(entry.good()); | |
4014 EXPECT_TRUE(entry.GetIsDel()); | |
4015 EXPECT_FALSE(entry.GetServerIsDel()); | |
4016 EXPECT_TRUE(entry.GetIsUnappliedUpdate()); | |
4017 EXPECT_NE(metahandle, entry.GetMetahandle()); | |
4018 } | |
4019 } | |
4020 | |
4021 TEST_F(SyncerTest, TestMoveSanitizedNamedFolder) { | |
4022 mock_server_->AddUpdateDirectory(1, 0, "foo", 1, 1, | |
4023 foreign_cache_guid(), "-1"); | |
4024 mock_server_->AddUpdateDirectory(2, 0, ":::", 1, 2, | |
4025 foreign_cache_guid(), "-2"); | |
4026 EXPECT_TRUE(SyncShareNudge()); | |
4027 { | |
4028 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
4029 MutableEntry entry(&trans, GET_BY_ID, ids_.FromNumber(2)); | |
4030 ASSERT_TRUE(entry.good()); | |
4031 entry.PutParentId(ids_.FromNumber(1)); | |
4032 EXPECT_TRUE(entry.PutIsUnsynced(true)); | |
4033 } | |
4034 EXPECT_TRUE(SyncShareNudge()); | |
4035 // We use the same sync ts as before so our times match up. | |
4036 mock_server_->AddUpdateDirectory(2, 1, ":::", 2, 2, | |
4037 foreign_cache_guid(), "-2"); | |
4038 EXPECT_TRUE(SyncShareNudge()); | |
4039 } | |
4040 | |
4041 // Don't crash when this occurs. | |
4042 TEST_F(SyncerTest, UpdateWhereParentIsNotAFolder) { | |
4043 mock_server_->AddUpdateBookmark(1, 0, "B", 10, 10, | |
4044 foreign_cache_guid(), "-1"); | |
4045 mock_server_->AddUpdateDirectory(2, 1, "BookmarkParent", 10, 10, | |
4046 foreign_cache_guid(), "-2"); | |
4047 // Used to cause a CHECK | |
4048 EXPECT_TRUE(SyncShareNudge()); | |
4049 { | |
4050 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
4051 Entry good_entry(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(1)); | |
4052 ASSERT_TRUE(good_entry.good()); | |
4053 EXPECT_FALSE(good_entry.GetIsUnappliedUpdate()); | |
4054 Entry bad_parent(&rtrans, syncable::GET_BY_ID, ids_.FromNumber(2)); | |
4055 ASSERT_TRUE(bad_parent.good()); | |
4056 EXPECT_TRUE(bad_parent.GetIsUnappliedUpdate()); | |
4057 } | |
4058 } | |
4059 | |
4060 TEST_F(SyncerTest, DirectoryUpdateTest) { | |
4061 Id in_root_id = ids_.NewServerId(); | |
4062 Id in_in_root_id = ids_.NewServerId(); | |
4063 | |
4064 mock_server_->AddUpdateDirectory(in_root_id, TestIdFactory::root(), | |
4065 "in_root_name", 2, 2, | |
4066 foreign_cache_guid(), "-1"); | |
4067 mock_server_->AddUpdateDirectory(in_in_root_id, in_root_id, | |
4068 "in_in_root_name", 3, 3, | |
4069 foreign_cache_guid(), "-2"); | |
4070 EXPECT_TRUE(SyncShareNudge()); | |
4071 { | |
4072 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4073 Entry in_root(&trans, GET_BY_ID, in_root_id); | |
4074 ASSERT_TRUE(in_root.good()); | |
4075 EXPECT_EQ("in_root_name", in_root.GetNonUniqueName()); | |
4076 EXPECT_EQ(TestIdFactory::root(), in_root.GetParentId()); | |
4077 | |
4078 Entry in_in_root(&trans, GET_BY_ID, in_in_root_id); | |
4079 ASSERT_TRUE(in_in_root.good()); | |
4080 EXPECT_EQ("in_in_root_name", in_in_root.GetNonUniqueName()); | |
4081 EXPECT_EQ(in_root_id, in_in_root.GetParentId()); | |
4082 } | |
4083 } | |
4084 | |
4085 TEST_F(SyncerTest, DirectoryCommitTest) { | |
4086 syncable::Id in_root_id, in_dir_id; | |
4087 int64_t foo_metahandle; | |
4088 int64_t bar_metahandle; | |
4089 | |
4090 { | |
4091 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
4092 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root_id_, "foo"); | |
4093 ASSERT_TRUE(parent.good()); | |
4094 parent.PutIsUnsynced(true); | |
4095 parent.PutIsDir(true); | |
4096 parent.PutSpecifics(DefaultBookmarkSpecifics()); | |
4097 in_root_id = parent.GetId(); | |
4098 foo_metahandle = parent.GetMetahandle(); | |
4099 | |
4100 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "bar"); | |
4101 ASSERT_TRUE(child.good()); | |
4102 child.PutIsUnsynced(true); | |
4103 child.PutIsDir(true); | |
4104 child.PutSpecifics(DefaultBookmarkSpecifics()); | |
4105 bar_metahandle = child.GetMetahandle(); | |
4106 in_dir_id = parent.GetId(); | |
4107 } | |
4108 EXPECT_TRUE(SyncShareNudge()); | |
4109 { | |
4110 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4111 Entry fail_by_old_id_entry(&trans, GET_BY_ID, in_root_id); | |
4112 ASSERT_FALSE(fail_by_old_id_entry.good()); | |
4113 | |
4114 Entry foo_entry(&trans, GET_BY_HANDLE, foo_metahandle); | |
4115 ASSERT_TRUE(foo_entry.good()); | |
4116 EXPECT_EQ("foo", foo_entry.GetNonUniqueName()); | |
4117 EXPECT_NE(in_root_id, foo_entry.GetId()); | |
4118 | |
4119 Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle); | |
4120 ASSERT_TRUE(bar_entry.good()); | |
4121 EXPECT_EQ("bar", bar_entry.GetNonUniqueName()); | |
4122 EXPECT_NE(in_dir_id, bar_entry.GetId()); | |
4123 EXPECT_EQ(foo_entry.GetId(), bar_entry.GetParentId()); | |
4124 } | |
4125 } | |
4126 | |
4127 TEST_F(SyncerTest, TestClientCommandDuringUpdate) { | |
4128 using sync_pb::ClientCommand; | |
4129 | |
4130 ClientCommand* command = new ClientCommand(); | |
4131 command->set_set_sync_poll_interval(8); | |
4132 command->set_set_sync_long_poll_interval(800); | |
4133 command->set_sessions_commit_delay_seconds(3141); | |
4134 sync_pb::CustomNudgeDelay* bookmark_delay = | |
4135 command->add_custom_nudge_delays(); | |
4136 bookmark_delay->set_datatype_id( | |
4137 GetSpecificsFieldNumberFromModelType(BOOKMARKS)); | |
4138 bookmark_delay->set_delay_ms(950); | |
4139 command->set_client_invalidation_hint_buffer_size(11); | |
4140 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1, | |
4141 foreign_cache_guid(), "-1"); | |
4142 mock_server_->SetGUClientCommand(command); | |
4143 EXPECT_TRUE(SyncShareNudge()); | |
4144 | |
4145 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_); | |
4146 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_); | |
4147 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_); | |
4148 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_); | |
4149 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_); | |
4150 | |
4151 command = new ClientCommand(); | |
4152 command->set_set_sync_poll_interval(180); | |
4153 command->set_set_sync_long_poll_interval(190); | |
4154 command->set_sessions_commit_delay_seconds(2718); | |
4155 bookmark_delay = command->add_custom_nudge_delays(); | |
4156 bookmark_delay->set_datatype_id( | |
4157 GetSpecificsFieldNumberFromModelType(BOOKMARKS)); | |
4158 bookmark_delay->set_delay_ms(1050); | |
4159 command->set_client_invalidation_hint_buffer_size(9); | |
4160 mock_server_->AddUpdateDirectory( | |
4161 1, 0, "in_root", 1, 1, foreign_cache_guid(), "-1"); | |
4162 mock_server_->SetGUClientCommand(command); | |
4163 EXPECT_TRUE(SyncShareNudge()); | |
4164 | |
4165 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_); | |
4166 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_); | |
4167 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_); | |
4168 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_); | |
4169 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_); | |
4170 } | |
4171 | |
4172 TEST_F(SyncerTest, TestClientCommandDuringCommit) { | |
4173 using sync_pb::ClientCommand; | |
4174 | |
4175 ClientCommand* command = new ClientCommand(); | |
4176 command->set_set_sync_poll_interval(8); | |
4177 command->set_set_sync_long_poll_interval(800); | |
4178 command->set_sessions_commit_delay_seconds(3141); | |
4179 sync_pb::CustomNudgeDelay* bookmark_delay = | |
4180 command->add_custom_nudge_delays(); | |
4181 bookmark_delay->set_datatype_id( | |
4182 GetSpecificsFieldNumberFromModelType(BOOKMARKS)); | |
4183 bookmark_delay->set_delay_ms(950); | |
4184 command->set_client_invalidation_hint_buffer_size(11); | |
4185 CreateUnsyncedDirectory("X", "id_X"); | |
4186 mock_server_->SetCommitClientCommand(command); | |
4187 EXPECT_TRUE(SyncShareNudge()); | |
4188 | |
4189 EXPECT_EQ(TimeDelta::FromSeconds(8), last_short_poll_interval_received_); | |
4190 EXPECT_EQ(TimeDelta::FromSeconds(800), last_long_poll_interval_received_); | |
4191 EXPECT_EQ(TimeDelta::FromSeconds(3141), last_sessions_commit_delay_); | |
4192 EXPECT_EQ(TimeDelta::FromMilliseconds(950), last_bookmarks_commit_delay_); | |
4193 EXPECT_EQ(11, last_client_invalidation_hint_buffer_size_); | |
4194 | |
4195 command = new ClientCommand(); | |
4196 command->set_set_sync_poll_interval(180); | |
4197 command->set_set_sync_long_poll_interval(190); | |
4198 command->set_sessions_commit_delay_seconds(2718); | |
4199 bookmark_delay = command->add_custom_nudge_delays(); | |
4200 bookmark_delay->set_datatype_id( | |
4201 GetSpecificsFieldNumberFromModelType(BOOKMARKS)); | |
4202 bookmark_delay->set_delay_ms(1050); | |
4203 command->set_client_invalidation_hint_buffer_size(9); | |
4204 CreateUnsyncedDirectory("Y", "id_Y"); | |
4205 mock_server_->SetCommitClientCommand(command); | |
4206 EXPECT_TRUE(SyncShareNudge()); | |
4207 | |
4208 EXPECT_EQ(TimeDelta::FromSeconds(180), last_short_poll_interval_received_); | |
4209 EXPECT_EQ(TimeDelta::FromSeconds(190), last_long_poll_interval_received_); | |
4210 EXPECT_EQ(TimeDelta::FromSeconds(2718), last_sessions_commit_delay_); | |
4211 EXPECT_EQ(TimeDelta::FromMilliseconds(1050), last_bookmarks_commit_delay_); | |
4212 EXPECT_EQ(9, last_client_invalidation_hint_buffer_size_); | |
4213 } | |
4214 | |
4215 TEST_F(SyncerTest, EnsureWeSendUpOldParent) { | |
4216 syncable::Id folder_one_id = ids_.FromNumber(1); | |
4217 syncable::Id folder_two_id = ids_.FromNumber(2); | |
4218 | |
4219 mock_server_->AddUpdateDirectory(folder_one_id, TestIdFactory::root(), | |
4220 "folder_one", 1, 1, foreign_cache_guid(), "-1"); | |
4221 mock_server_->AddUpdateDirectory(folder_two_id, TestIdFactory::root(), | |
4222 "folder_two", 1, 1, foreign_cache_guid(), "-2"); | |
4223 EXPECT_TRUE(SyncShareNudge()); | |
4224 { | |
4225 // A moved entry should send an "old parent." | |
4226 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
4227 MutableEntry entry(&trans, GET_BY_ID, folder_one_id); | |
4228 ASSERT_TRUE(entry.good()); | |
4229 entry.PutParentId(folder_two_id); | |
4230 entry.PutIsUnsynced(true); | |
4231 // A new entry should send no "old parent." | |
4232 MutableEntry create( | |
4233 &trans, CREATE, BOOKMARKS, trans.root_id(), "new_folder"); | |
4234 create.PutIsUnsynced(true); | |
4235 create.PutSpecifics(DefaultBookmarkSpecifics()); | |
4236 } | |
4237 EXPECT_TRUE(SyncShareNudge()); | |
4238 const sync_pb::CommitMessage& commit = mock_server_->last_sent_commit(); | |
4239 ASSERT_EQ(2, commit.entries_size()); | |
4240 EXPECT_EQ("2", commit.entries(0).parent_id_string()); | |
4241 EXPECT_EQ("0", commit.entries(0).old_parent_id()); | |
4242 EXPECT_FALSE(commit.entries(1).has_old_parent_id()); | |
4243 } | |
4244 | |
4245 TEST_F(SyncerTest, Test64BitVersionSupport) { | |
4246 int64_t really_big_int = std::numeric_limits<int64_t>::max() - 12; | |
4247 const string name("ringo's dang orang ran rings around my o-ring"); | |
4248 int64_t item_metahandle; | |
4249 | |
4250 // Try writing max int64_t to the version fields of a meta entry. | |
4251 { | |
4252 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
4253 MutableEntry entry(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name); | |
4254 ASSERT_TRUE(entry.good()); | |
4255 entry.PutBaseVersion(really_big_int); | |
4256 entry.PutServerVersion(really_big_int); | |
4257 entry.PutId(ids_.NewServerId()); | |
4258 item_metahandle = entry.GetMetahandle(); | |
4259 } | |
4260 // Now read it back out and make sure the value is max int64_t. | |
4261 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
4262 Entry entry(&rtrans, syncable::GET_BY_HANDLE, item_metahandle); | |
4263 ASSERT_TRUE(entry.good()); | |
4264 EXPECT_EQ(really_big_int, entry.GetBaseVersion()); | |
4265 } | |
4266 | |
4267 TEST_F(SyncerTest, TestSimpleUndelete) { | |
4268 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root(); | |
4269 mock_server_->set_conflict_all_commits(true); | |
4270 // Let there be an entry from the server. | |
4271 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10, | |
4272 foreign_cache_guid(), "-1"); | |
4273 EXPECT_TRUE(SyncShareNudge()); | |
4274 // Check it out and delete it. | |
4275 { | |
4276 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
4277 MutableEntry entry(&wtrans, GET_BY_ID, id); | |
4278 ASSERT_TRUE(entry.good()); | |
4279 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
4280 EXPECT_FALSE(entry.GetIsUnsynced()); | |
4281 EXPECT_FALSE(entry.GetIsDel()); | |
4282 // Delete it locally. | |
4283 entry.PutIsDel(true); | |
4284 } | |
4285 EXPECT_TRUE(SyncShareNudge()); | |
4286 // Confirm we see IS_DEL and not SERVER_IS_DEL. | |
4287 { | |
4288 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4289 Entry entry(&trans, GET_BY_ID, id); | |
4290 ASSERT_TRUE(entry.good()); | |
4291 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
4292 EXPECT_FALSE(entry.GetIsUnsynced()); | |
4293 EXPECT_TRUE(entry.GetIsDel()); | |
4294 EXPECT_FALSE(entry.GetServerIsDel()); | |
4295 } | |
4296 EXPECT_TRUE(SyncShareNudge()); | |
4297 // Update from server confirming deletion. | |
4298 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 11, | |
4299 foreign_cache_guid(), "-1"); | |
4300 mock_server_->SetLastUpdateDeleted(); | |
4301 EXPECT_TRUE(SyncShareNudge()); | |
4302 // IS_DEL AND SERVER_IS_DEL now both true. | |
4303 { | |
4304 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4305 Entry entry(&trans, GET_BY_ID, id); | |
4306 ASSERT_TRUE(entry.good()); | |
4307 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
4308 EXPECT_FALSE(entry.GetIsUnsynced()); | |
4309 EXPECT_TRUE(entry.GetIsDel()); | |
4310 EXPECT_TRUE(entry.GetServerIsDel()); | |
4311 } | |
4312 // Undelete from server. | |
4313 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12, | |
4314 foreign_cache_guid(), "-1"); | |
4315 EXPECT_TRUE(SyncShareNudge()); | |
4316 // IS_DEL and SERVER_IS_DEL now both false. | |
4317 { | |
4318 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4319 Entry entry(&trans, GET_BY_ID, id); | |
4320 ASSERT_TRUE(entry.good()); | |
4321 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
4322 EXPECT_FALSE(entry.GetIsUnsynced()); | |
4323 EXPECT_FALSE(entry.GetIsDel()); | |
4324 EXPECT_FALSE(entry.GetServerIsDel()); | |
4325 } | |
4326 } | |
4327 | |
4328 TEST_F(SyncerTest, TestUndeleteWithMissingDeleteUpdate) { | |
4329 Id id = ids_.MakeServer("undeletion item"), root = TestIdFactory::root(); | |
4330 // Let there be a entry, from the server. | |
4331 mock_server_->set_conflict_all_commits(true); | |
4332 mock_server_->AddUpdateBookmark(id, root, "foo", 1, 10, | |
4333 foreign_cache_guid(), "-1"); | |
4334 EXPECT_TRUE(SyncShareNudge()); | |
4335 // Check it out and delete it. | |
4336 { | |
4337 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
4338 MutableEntry entry(&wtrans, GET_BY_ID, id); | |
4339 ASSERT_TRUE(entry.good()); | |
4340 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
4341 EXPECT_FALSE(entry.GetIsUnsynced()); | |
4342 EXPECT_FALSE(entry.GetIsDel()); | |
4343 // Delete it locally. | |
4344 entry.PutIsDel(true); | |
4345 } | |
4346 EXPECT_TRUE(SyncShareNudge()); | |
4347 // Confirm we see IS_DEL and not SERVER_IS_DEL. | |
4348 { | |
4349 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4350 Entry entry(&trans, GET_BY_ID, id); | |
4351 ASSERT_TRUE(entry.good()); | |
4352 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
4353 EXPECT_FALSE(entry.GetIsUnsynced()); | |
4354 EXPECT_TRUE(entry.GetIsDel()); | |
4355 EXPECT_FALSE(entry.GetServerIsDel()); | |
4356 } | |
4357 EXPECT_TRUE(SyncShareNudge()); | |
4358 // Say we do not get an update from server confirming deletion. Undelete | |
4359 // from server | |
4360 mock_server_->AddUpdateBookmark(id, root, "foo", 2, 12, | |
4361 foreign_cache_guid(), "-1"); | |
4362 EXPECT_TRUE(SyncShareNudge()); | |
4363 // IS_DEL and SERVER_IS_DEL now both false. | |
4364 { | |
4365 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4366 Entry entry(&trans, GET_BY_ID, id); | |
4367 ASSERT_TRUE(entry.good()); | |
4368 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
4369 EXPECT_FALSE(entry.GetIsUnsynced()); | |
4370 EXPECT_FALSE(entry.GetIsDel()); | |
4371 EXPECT_FALSE(entry.GetServerIsDel()); | |
4372 } | |
4373 } | |
4374 | |
4375 TEST_F(SyncerTest, TestUndeleteIgnoreCorrectlyUnappliedUpdate) { | |
4376 Id id1 = ids_.MakeServer("first"), id2 = ids_.MakeServer("second"); | |
4377 Id root = TestIdFactory::root(); | |
4378 // Duplicate! expect path clashing! | |
4379 mock_server_->set_conflict_all_commits(true); | |
4380 mock_server_->AddUpdateBookmark(id1, root, "foo", 1, 10, | |
4381 foreign_cache_guid(), "-1"); | |
4382 mock_server_->AddUpdateBookmark(id2, root, "foo", 1, 10, | |
4383 foreign_cache_guid(), "-2"); | |
4384 EXPECT_TRUE(SyncShareNudge()); | |
4385 mock_server_->AddUpdateBookmark(id2, root, "foo2", 2, 20, | |
4386 foreign_cache_guid(), "-2"); | |
4387 EXPECT_TRUE(SyncShareNudge()); // Now just don't explode. | |
4388 } | |
4389 | |
4390 TEST_F(SyncerTest, ClientTagServerCreatedUpdatesWork) { | |
4391 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10, | |
4392 foreign_cache_guid(), "-1"); | |
4393 mock_server_->SetLastUpdateClientTag("permfolder"); | |
4394 | |
4395 EXPECT_TRUE(SyncShareNudge()); | |
4396 | |
4397 { | |
4398 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4399 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder"); | |
4400 ASSERT_TRUE(perm_folder.good()); | |
4401 EXPECT_FALSE(perm_folder.GetIsDel()); | |
4402 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); | |
4403 EXPECT_FALSE(perm_folder.GetIsUnsynced()); | |
4404 EXPECT_EQ("permfolder", perm_folder.GetUniqueClientTag()); | |
4405 EXPECT_EQ("permitem1", perm_folder.GetNonUniqueName()); | |
4406 } | |
4407 | |
4408 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100, | |
4409 foreign_cache_guid(), "-1"); | |
4410 mock_server_->SetLastUpdateClientTag("permfolder"); | |
4411 EXPECT_TRUE(SyncShareNudge()); | |
4412 | |
4413 { | |
4414 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4415 | |
4416 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder"); | |
4417 ASSERT_TRUE(perm_folder.good()); | |
4418 EXPECT_FALSE(perm_folder.GetIsDel()); | |
4419 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); | |
4420 EXPECT_FALSE(perm_folder.GetIsUnsynced()); | |
4421 EXPECT_EQ("permfolder", perm_folder.GetUniqueClientTag()); | |
4422 EXPECT_EQ("permitem_renamed", perm_folder.GetNonUniqueName()); | |
4423 } | |
4424 } | |
4425 | |
4426 TEST_F(SyncerTest, ClientTagIllegalUpdateIgnored) { | |
4427 mock_server_->AddUpdateDirectory(1, 0, "permitem1", 1, 10, | |
4428 foreign_cache_guid(), "-1"); | |
4429 mock_server_->SetLastUpdateClientTag("permfolder"); | |
4430 | |
4431 EXPECT_TRUE(SyncShareNudge()); | |
4432 | |
4433 { | |
4434 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4435 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder"); | |
4436 ASSERT_TRUE(perm_folder.good()); | |
4437 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); | |
4438 EXPECT_FALSE(perm_folder.GetIsUnsynced()); | |
4439 EXPECT_EQ("permfolder", perm_folder.GetUniqueClientTag()); | |
4440 EXPECT_EQ("permitem1", perm_folder.GetNonUniqueName()); | |
4441 EXPECT_TRUE(perm_folder.GetId().ServerKnows()); | |
4442 } | |
4443 | |
4444 mock_server_->AddUpdateDirectory(1, 0, "permitem_renamed", 10, 100, | |
4445 foreign_cache_guid(), "-1"); | |
4446 mock_server_->SetLastUpdateClientTag("wrongtag"); | |
4447 EXPECT_TRUE(SyncShareNudge()); | |
4448 | |
4449 { | |
4450 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4451 | |
4452 // This update is rejected because it has the same ID, but a | |
4453 // different tag than one that is already on the client. | |
4454 // The client has a ServerKnows ID, which cannot be overwritten. | |
4455 Entry rejected_update(&trans, GET_BY_CLIENT_TAG, "wrongtag"); | |
4456 EXPECT_FALSE(rejected_update.good()); | |
4457 | |
4458 Entry perm_folder(&trans, GET_BY_CLIENT_TAG, "permfolder"); | |
4459 ASSERT_TRUE(perm_folder.good()); | |
4460 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); | |
4461 EXPECT_FALSE(perm_folder.GetIsUnsynced()); | |
4462 EXPECT_EQ("permitem1", perm_folder.GetNonUniqueName()); | |
4463 } | |
4464 } | |
4465 | |
4466 TEST_F(SyncerTest, ClientTagUncommittedTagMatchesUpdate) { | |
4467 int64_t original_metahandle = 0; | |
4468 | |
4469 { | |
4470 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
4471 MutableEntry pref( | |
4472 &trans, CREATE, PREFERENCES, ids_.root(), "name"); | |
4473 ASSERT_TRUE(pref.good()); | |
4474 pref.PutUniqueClientTag("tag"); | |
4475 pref.PutIsUnsynced(true); | |
4476 EXPECT_FALSE(pref.GetIsUnappliedUpdate()); | |
4477 EXPECT_FALSE(pref.GetId().ServerKnows()); | |
4478 original_metahandle = pref.GetMetahandle(); | |
4479 } | |
4480 | |
4481 syncable::Id server_id = TestIdFactory::MakeServer("id"); | |
4482 mock_server_->AddUpdatePref(server_id.GetServerId(), | |
4483 ids_.root().GetServerId(), | |
4484 "tag", 10, 100); | |
4485 mock_server_->set_conflict_all_commits(true); | |
4486 | |
4487 EXPECT_FALSE(SyncShareNudge()); | |
4488 // This should cause client tag reunion, preserving the metahandle. | |
4489 { | |
4490 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4491 | |
4492 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag"); | |
4493 ASSERT_TRUE(pref.good()); | |
4494 EXPECT_FALSE(pref.GetIsDel()); | |
4495 EXPECT_FALSE(pref.GetIsUnappliedUpdate()); | |
4496 EXPECT_TRUE(pref.GetIsUnsynced()); | |
4497 EXPECT_EQ(10, pref.GetBaseVersion()); | |
4498 // Entry should have been given the new ID while preserving the | |
4499 // metahandle; client should have won the conflict resolution. | |
4500 EXPECT_EQ(original_metahandle, pref.GetMetahandle()); | |
4501 EXPECT_EQ("tag", pref.GetUniqueClientTag()); | |
4502 EXPECT_TRUE(pref.GetId().ServerKnows()); | |
4503 } | |
4504 | |
4505 mock_server_->set_conflict_all_commits(false); | |
4506 EXPECT_TRUE(SyncShareNudge()); | |
4507 | |
4508 // The resolved entry ought to commit cleanly. | |
4509 { | |
4510 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4511 | |
4512 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag"); | |
4513 ASSERT_TRUE(pref.good()); | |
4514 EXPECT_FALSE(pref.GetIsDel()); | |
4515 EXPECT_FALSE(pref.GetIsUnappliedUpdate()); | |
4516 EXPECT_FALSE(pref.GetIsUnsynced()); | |
4517 EXPECT_LT(10, pref.GetBaseVersion()); | |
4518 // Entry should have been given the new ID while preserving the | |
4519 // metahandle; client should have won the conflict resolution. | |
4520 EXPECT_EQ(original_metahandle, pref.GetMetahandle()); | |
4521 EXPECT_EQ("tag", pref.GetUniqueClientTag()); | |
4522 EXPECT_TRUE(pref.GetId().ServerKnows()); | |
4523 } | |
4524 } | |
4525 | |
4526 TEST_F(SyncerTest, ClientTagConflictWithDeletedLocalEntry) { | |
4527 { | |
4528 // Create a deleted local entry with a unique client tag. | |
4529 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
4530 MutableEntry pref( | |
4531 &trans, CREATE, PREFERENCES, ids_.root(), "name"); | |
4532 ASSERT_TRUE(pref.good()); | |
4533 ASSERT_FALSE(pref.GetId().ServerKnows()); | |
4534 pref.PutUniqueClientTag("tag"); | |
4535 pref.PutIsUnsynced(true); | |
4536 | |
4537 // Note: IS_DEL && !ServerKnows() will clear the UNSYNCED bit. | |
4538 // (We never attempt to commit server-unknown deleted items, so this | |
4539 // helps us clean up those entries). | |
4540 pref.PutIsDel(true); | |
4541 } | |
4542 | |
4543 // Prepare an update with the same unique client tag. | |
4544 syncable::Id server_id = TestIdFactory::MakeServer("id"); | |
4545 mock_server_->AddUpdatePref(server_id.GetServerId(), | |
4546 ids_.root().GetServerId(), | |
4547 "tag", 10, 100); | |
4548 | |
4549 EXPECT_TRUE(SyncShareNudge()); | |
4550 // The local entry will be overwritten. | |
4551 { | |
4552 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4553 | |
4554 Entry pref(&trans, GET_BY_CLIENT_TAG, "tag"); | |
4555 ASSERT_TRUE(pref.good()); | |
4556 ASSERT_TRUE(pref.GetId().ServerKnows()); | |
4557 EXPECT_FALSE(pref.GetIsDel()); | |
4558 EXPECT_FALSE(pref.GetIsUnappliedUpdate()); | |
4559 EXPECT_FALSE(pref.GetIsUnsynced()); | |
4560 EXPECT_EQ(10, pref.GetBaseVersion()); | |
4561 EXPECT_EQ("tag", pref.GetUniqueClientTag()); | |
4562 } | |
4563 } | |
4564 | |
4565 TEST_F(SyncerTest, ClientTagUpdateClashesWithLocalEntry) { | |
4566 // This test is written assuming that ID comparison | |
4567 // will work out in a particular way. | |
4568 EXPECT_LT(ids_.FromNumber(1), ids_.FromNumber(2)); | |
4569 EXPECT_LT(ids_.FromNumber(3), ids_.FromNumber(4)); | |
4570 | |
4571 syncable::Id id1 = TestIdFactory::MakeServer("1"); | |
4572 mock_server_->AddUpdatePref(id1.GetServerId(), "", "tag1", 10, 100); | |
4573 | |
4574 syncable::Id id4 = TestIdFactory::MakeServer("4"); | |
4575 mock_server_->AddUpdatePref(id4.GetServerId(), "", "tag2", 11, 110); | |
4576 | |
4577 mock_server_->set_conflict_all_commits(true); | |
4578 | |
4579 EXPECT_TRUE(SyncShareNudge()); | |
4580 int64_t tag1_metahandle = syncable::kInvalidMetaHandle; | |
4581 int64_t tag2_metahandle = syncable::kInvalidMetaHandle; | |
4582 // This should cause client tag overwrite. | |
4583 { | |
4584 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4585 | |
4586 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1"); | |
4587 ASSERT_TRUE(tag1.good()); | |
4588 ASSERT_TRUE(tag1.GetId().ServerKnows()); | |
4589 ASSERT_EQ(id1, tag1.GetId()); | |
4590 EXPECT_FALSE(tag1.GetIsDel()); | |
4591 EXPECT_FALSE(tag1.GetIsUnappliedUpdate()); | |
4592 EXPECT_FALSE(tag1.GetIsUnsynced()); | |
4593 EXPECT_EQ(10, tag1.GetBaseVersion()); | |
4594 EXPECT_EQ("tag1", tag1.GetUniqueClientTag()); | |
4595 tag1_metahandle = tag1.GetMetahandle(); | |
4596 | |
4597 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2"); | |
4598 ASSERT_TRUE(tag2.good()); | |
4599 ASSERT_TRUE(tag2.GetId().ServerKnows()); | |
4600 ASSERT_EQ(id4, tag2.GetId()); | |
4601 EXPECT_FALSE(tag2.GetIsDel()); | |
4602 EXPECT_FALSE(tag2.GetIsUnappliedUpdate()); | |
4603 EXPECT_FALSE(tag2.GetIsUnsynced()); | |
4604 EXPECT_EQ(11, tag2.GetBaseVersion()); | |
4605 EXPECT_EQ("tag2", tag2.GetUniqueClientTag()); | |
4606 tag2_metahandle = tag2.GetMetahandle(); | |
4607 | |
4608 // Preferences type root should have been created by the updates above. | |
4609 ASSERT_TRUE(directory()->InitialSyncEndedForType(&trans, PREFERENCES)); | |
4610 | |
4611 Entry pref_root(&trans, GET_TYPE_ROOT, PREFERENCES); | |
4612 ASSERT_TRUE(pref_root.good()); | |
4613 | |
4614 syncable::Directory::Metahandles children; | |
4615 directory()->GetChildHandlesById(&trans, pref_root.GetId(), &children); | |
4616 ASSERT_EQ(2U, children.size()); | |
4617 } | |
4618 | |
4619 syncable::Id id2 = TestIdFactory::MakeServer("2"); | |
4620 mock_server_->AddUpdatePref(id2.GetServerId(), "", "tag1", 12, 120); | |
4621 syncable::Id id3 = TestIdFactory::MakeServer("3"); | |
4622 mock_server_->AddUpdatePref(id3.GetServerId(), "", "tag2", 13, 130); | |
4623 EXPECT_TRUE(SyncShareNudge()); | |
4624 | |
4625 { | |
4626 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4627 | |
4628 Entry tag1(&trans, GET_BY_CLIENT_TAG, "tag1"); | |
4629 ASSERT_TRUE(tag1.good()); | |
4630 ASSERT_TRUE(tag1.GetId().ServerKnows()); | |
4631 ASSERT_EQ(id1, tag1.GetId()) | |
4632 << "ID 1 should be kept, since it was less than ID 2."; | |
4633 EXPECT_FALSE(tag1.GetIsDel()); | |
4634 EXPECT_FALSE(tag1.GetIsUnappliedUpdate()); | |
4635 EXPECT_FALSE(tag1.GetIsUnsynced()); | |
4636 EXPECT_EQ(10, tag1.GetBaseVersion()); | |
4637 EXPECT_EQ("tag1", tag1.GetUniqueClientTag()); | |
4638 EXPECT_EQ(tag1_metahandle, tag1.GetMetahandle()); | |
4639 | |
4640 Entry tag2(&trans, GET_BY_CLIENT_TAG, "tag2"); | |
4641 ASSERT_TRUE(tag2.good()); | |
4642 ASSERT_TRUE(tag2.GetId().ServerKnows()); | |
4643 ASSERT_EQ(id3, tag2.GetId()) | |
4644 << "ID 3 should be kept, since it was less than ID 4."; | |
4645 EXPECT_FALSE(tag2.GetIsDel()); | |
4646 EXPECT_FALSE(tag2.GetIsUnappliedUpdate()); | |
4647 EXPECT_FALSE(tag2.GetIsUnsynced()); | |
4648 EXPECT_EQ(13, tag2.GetBaseVersion()); | |
4649 EXPECT_EQ("tag2", tag2.GetUniqueClientTag()); | |
4650 EXPECT_EQ(tag2_metahandle, tag2.GetMetahandle()); | |
4651 | |
4652 // Preferences type root should have been created by the updates above. | |
4653 ASSERT_TRUE(directory()->InitialSyncEndedForType(&trans, PREFERENCES)); | |
4654 | |
4655 Entry pref_root(&trans, GET_TYPE_ROOT, PREFERENCES); | |
4656 ASSERT_TRUE(pref_root.good()); | |
4657 | |
4658 syncable::Directory::Metahandles children; | |
4659 directory()->GetChildHandlesById(&trans, pref_root.GetId(), &children); | |
4660 ASSERT_EQ(2U, children.size()); | |
4661 } | |
4662 } | |
4663 | |
4664 TEST_F(SyncerTest, ClientTagClashWithinBatchOfUpdates) { | |
4665 // This test is written assuming that ID comparison | |
4666 // will work out in a particular way. | |
4667 EXPECT_LT(ids_.FromNumber(1), ids_.FromNumber(4)); | |
4668 EXPECT_LT(ids_.FromNumber(201), ids_.FromNumber(205)); | |
4669 | |
4670 // Least ID: winner. | |
4671 mock_server_->AddUpdatePref(ids_.FromNumber(1).GetServerId(), "", "tag a", 1, | |
4672 10); | |
4673 mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(), "", "tag a", 11, | |
4674 110); | |
4675 mock_server_->AddUpdatePref(ids_.FromNumber(3).GetServerId(), "", "tag a", 12, | |
4676 120); | |
4677 mock_server_->AddUpdatePref(ids_.FromNumber(4).GetServerId(), "", "tag a", 13, | |
4678 130); | |
4679 mock_server_->AddUpdatePref(ids_.FromNumber(105).GetServerId(), "", "tag b", | |
4680 14, 140); | |
4681 mock_server_->AddUpdatePref(ids_.FromNumber(102).GetServerId(), "", "tag b", | |
4682 15, 150); | |
4683 // Least ID: winner. | |
4684 mock_server_->AddUpdatePref(ids_.FromNumber(101).GetServerId(), "", "tag b", | |
4685 16, 160); | |
4686 mock_server_->AddUpdatePref(ids_.FromNumber(104).GetServerId(), "", "tag b", | |
4687 17, 170); | |
4688 | |
4689 mock_server_->AddUpdatePref(ids_.FromNumber(205).GetServerId(), "", "tag c", | |
4690 18, 180); | |
4691 mock_server_->AddUpdatePref(ids_.FromNumber(202).GetServerId(), "", "tag c", | |
4692 19, 190); | |
4693 mock_server_->AddUpdatePref(ids_.FromNumber(204).GetServerId(), "", "tag c", | |
4694 20, 200); | |
4695 // Least ID: winner. | |
4696 mock_server_->AddUpdatePref(ids_.FromNumber(201).GetServerId(), "", "tag c", | |
4697 21, 210); | |
4698 | |
4699 mock_server_->set_conflict_all_commits(true); | |
4700 | |
4701 EXPECT_TRUE(SyncShareNudge()); | |
4702 // This should cause client tag overwrite. | |
4703 { | |
4704 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4705 | |
4706 Entry tag_a(&trans, GET_BY_CLIENT_TAG, "tag a"); | |
4707 ASSERT_TRUE(tag_a.good()); | |
4708 EXPECT_TRUE(tag_a.GetId().ServerKnows()); | |
4709 EXPECT_EQ(ids_.FromNumber(1), tag_a.GetId()); | |
4710 EXPECT_FALSE(tag_a.GetIsDel()); | |
4711 EXPECT_FALSE(tag_a.GetIsUnappliedUpdate()); | |
4712 EXPECT_FALSE(tag_a.GetIsUnsynced()); | |
4713 EXPECT_EQ(1, tag_a.GetBaseVersion()); | |
4714 EXPECT_EQ("tag a", tag_a.GetUniqueClientTag()); | |
4715 | |
4716 Entry tag_b(&trans, GET_BY_CLIENT_TAG, "tag b"); | |
4717 ASSERT_TRUE(tag_b.good()); | |
4718 EXPECT_TRUE(tag_b.GetId().ServerKnows()); | |
4719 EXPECT_EQ(ids_.FromNumber(101), tag_b.GetId()); | |
4720 EXPECT_FALSE(tag_b.GetIsDel()); | |
4721 EXPECT_FALSE(tag_b.GetIsUnappliedUpdate()); | |
4722 EXPECT_FALSE(tag_b.GetIsUnsynced()); | |
4723 EXPECT_EQ(16, tag_b.GetBaseVersion()); | |
4724 EXPECT_EQ("tag b", tag_b.GetUniqueClientTag()); | |
4725 | |
4726 Entry tag_c(&trans, GET_BY_CLIENT_TAG, "tag c"); | |
4727 ASSERT_TRUE(tag_c.good()); | |
4728 EXPECT_TRUE(tag_c.GetId().ServerKnows()); | |
4729 EXPECT_EQ(ids_.FromNumber(201), tag_c.GetId()); | |
4730 EXPECT_FALSE(tag_c.GetIsDel()); | |
4731 EXPECT_FALSE(tag_c.GetIsUnappliedUpdate()); | |
4732 EXPECT_FALSE(tag_c.GetIsUnsynced()); | |
4733 EXPECT_EQ(21, tag_c.GetBaseVersion()); | |
4734 EXPECT_EQ("tag c", tag_c.GetUniqueClientTag()); | |
4735 | |
4736 // Preferences type root should have been created by the updates above. | |
4737 ASSERT_TRUE(directory()->InitialSyncEndedForType(&trans, PREFERENCES)); | |
4738 | |
4739 Entry pref_root(&trans, GET_TYPE_ROOT, PREFERENCES); | |
4740 ASSERT_TRUE(pref_root.good()); | |
4741 | |
4742 // Verify that we have exactly 3 tagged nodes under the type root. | |
4743 syncable::Directory::Metahandles children; | |
4744 directory()->GetChildHandlesById(&trans, pref_root.GetId(), &children); | |
4745 ASSERT_EQ(3U, children.size()); | |
4746 } | |
4747 } | |
4748 | |
4749 // This verifies transition to implicit permanent folders. | |
4750 TEST_F(SyncerTest, EntryWithParentIdUpdatedWithEntryWithoutParentId) { | |
4751 // Make sure SPECIFICS root exists so that we can get its parent ID. | |
4752 mock_server_->AddUpdateSpecifics(1, 0, "Folder", 10, 10, true, 1, | |
4753 DefaultPreferencesSpecifics()); | |
4754 mock_server_->SetLastUpdateServerTag(ModelTypeToRootTag(PREFERENCES)); | |
4755 EXPECT_TRUE(SyncShareNudge()); | |
4756 | |
4757 Id pref_root_id; | |
4758 { | |
4759 // Preferences type root should have been created by the update above. | |
4760 // We need it in order to get its ID. | |
4761 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4762 | |
4763 ASSERT_TRUE(directory()->InitialSyncEndedForType(&trans, PREFERENCES)); | |
4764 | |
4765 Entry pref_root(&trans, GET_TYPE_ROOT, PREFERENCES); | |
4766 ASSERT_TRUE(pref_root.good()); | |
4767 pref_root_id = pref_root.GetId(); | |
4768 } | |
4769 | |
4770 // Add a preference item with explicit parent ID. | |
4771 { | |
4772 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
4773 MutableEntry entry(&trans, CREATE, PREFERENCES, pref_root_id, "tag"); | |
4774 ASSERT_TRUE(entry.good()); | |
4775 entry.PutIsDir(false); | |
4776 entry.PutBaseVersion(1); | |
4777 entry.PutUniqueClientTag("tag"); | |
4778 entry.PutId(ids_.FromNumber(2)); | |
4779 } | |
4780 | |
4781 // Verify the entry above. | |
4782 { | |
4783 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4784 Entry pref_entry(&trans, GET_BY_CLIENT_TAG, "tag"); | |
4785 ASSERT_TRUE(pref_entry.good()); | |
4786 ASSERT_EQ(pref_root_id, pref_entry.GetParentId()); | |
4787 } | |
4788 | |
4789 // Make another update where the same item get updated, this time | |
4790 // with implicit parent ID. | |
4791 mock_server_->AddUpdatePref(ids_.FromNumber(2).GetServerId(), "", "tag", 2, | |
4792 20); | |
4793 | |
4794 EXPECT_TRUE(SyncShareNudge()); | |
4795 | |
4796 { | |
4797 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4798 Entry pref_entry(&trans, GET_BY_CLIENT_TAG, "tag"); | |
4799 ASSERT_TRUE(pref_entry.good()); | |
4800 ASSERT_TRUE(pref_entry.GetParentId().IsNull()); | |
4801 | |
4802 // Verify that there is still one node under the type root. | |
4803 syncable::Directory::Metahandles children; | |
4804 directory()->GetChildHandlesById(&trans, pref_root_id, &children); | |
4805 ASSERT_EQ(1U, children.size()); | |
4806 } | |
4807 } | |
4808 | |
4809 TEST_F(SyncerTest, UniqueServerTagUpdates) { | |
4810 // As a hurdle, introduce an item whose name is the same as the tag value | |
4811 // we'll use later. | |
4812 int64_t hurdle_handle = CreateUnsyncedDirectory("bob", "id_bob"); | |
4813 { | |
4814 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4815 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle); | |
4816 ASSERT_TRUE(hurdle.good()); | |
4817 ASSERT_TRUE(!hurdle.GetIsDel()); | |
4818 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty()); | |
4819 ASSERT_EQ("bob", hurdle.GetNonUniqueName()); | |
4820 | |
4821 // Try to lookup by the tagname. These should fail. | |
4822 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha"); | |
4823 EXPECT_FALSE(tag_alpha.good()); | |
4824 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob"); | |
4825 EXPECT_FALSE(tag_bob.good()); | |
4826 } | |
4827 | |
4828 // Now download some tagged items as updates. | |
4829 mock_server_->AddUpdateDirectory( | |
4830 1, 0, "update1", 1, 10, std::string(), std::string()); | |
4831 mock_server_->SetLastUpdateServerTag("alpha"); | |
4832 mock_server_->AddUpdateDirectory( | |
4833 2, 0, "update2", 2, 20, std::string(), std::string()); | |
4834 mock_server_->SetLastUpdateServerTag("bob"); | |
4835 EXPECT_TRUE(SyncShareNudge()); | |
4836 | |
4837 { | |
4838 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4839 | |
4840 // The new items should be applied as new entries, and we should be able | |
4841 // to look them up by their tag values. | |
4842 Entry tag_alpha(&trans, GET_BY_SERVER_TAG, "alpha"); | |
4843 ASSERT_TRUE(tag_alpha.good()); | |
4844 ASSERT_TRUE(!tag_alpha.GetIsDel()); | |
4845 ASSERT_EQ("alpha", tag_alpha.GetUniqueServerTag()); | |
4846 ASSERT_EQ("update1", tag_alpha.GetNonUniqueName()); | |
4847 Entry tag_bob(&trans, GET_BY_SERVER_TAG, "bob"); | |
4848 ASSERT_TRUE(tag_bob.good()); | |
4849 ASSERT_TRUE(!tag_bob.GetIsDel()); | |
4850 ASSERT_EQ("bob", tag_bob.GetUniqueServerTag()); | |
4851 ASSERT_EQ("update2", tag_bob.GetNonUniqueName()); | |
4852 // The old item should be unchanged. | |
4853 Entry hurdle(&trans, GET_BY_HANDLE, hurdle_handle); | |
4854 ASSERT_TRUE(hurdle.good()); | |
4855 ASSERT_TRUE(!hurdle.GetIsDel()); | |
4856 ASSERT_TRUE(hurdle.GetUniqueServerTag().empty()); | |
4857 ASSERT_EQ("bob", hurdle.GetNonUniqueName()); | |
4858 } | |
4859 } | |
4860 | |
4861 TEST_F(SyncerTest, GetUpdatesSetsRequestedTypes) { | |
4862 // The expectations of this test happen in the MockConnectionManager's | |
4863 // GetUpdates handler. EnableDatatype sets the expectation value from our | |
4864 // set of enabled/disabled datatypes. | |
4865 EnableDatatype(BOOKMARKS); | |
4866 EXPECT_TRUE(SyncShareNudge()); | |
4867 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
4868 | |
4869 EnableDatatype(AUTOFILL); | |
4870 EXPECT_TRUE(SyncShareNudge()); | |
4871 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
4872 | |
4873 EnableDatatype(PREFERENCES); | |
4874 EXPECT_TRUE(SyncShareNudge()); | |
4875 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
4876 | |
4877 DisableDatatype(BOOKMARKS); | |
4878 EXPECT_TRUE(SyncShareNudge()); | |
4879 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
4880 | |
4881 DisableDatatype(AUTOFILL); | |
4882 EXPECT_TRUE(SyncShareNudge()); | |
4883 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
4884 | |
4885 DisableDatatype(PREFERENCES); | |
4886 EnableDatatype(AUTOFILL); | |
4887 EXPECT_TRUE(SyncShareNudge()); | |
4888 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
4889 } | |
4890 | |
4891 // A typical scenario: server and client each have one update for the other. | |
4892 // This is the "happy path" alternative to UpdateFailsThenDontCommit. | |
4893 TEST_F(SyncerTest, UpdateThenCommit) { | |
4894 syncable::Id to_receive = ids_.NewServerId(); | |
4895 syncable::Id to_commit = ids_.NewLocalId(); | |
4896 | |
4897 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10, | |
4898 foreign_cache_guid(), "-1"); | |
4899 int64_t commit_handle = CreateUnsyncedDirectory("y", to_commit); | |
4900 EXPECT_TRUE(SyncShareNudge()); | |
4901 | |
4902 // The sync cycle should have included a GetUpdate, then a commit. By the | |
4903 // time the commit happened, we should have known for sure that there were no | |
4904 // hierarchy conflicts, and reported this fact to the server. | |
4905 ASSERT_TRUE(mock_server_->last_request().has_commit()); | |
4906 VerifyNoHierarchyConflictsReported(mock_server_->last_request()); | |
4907 | |
4908 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4909 | |
4910 Entry received(&trans, GET_BY_ID, to_receive); | |
4911 ASSERT_TRUE(received.good()); | |
4912 EXPECT_FALSE(received.GetIsUnsynced()); | |
4913 EXPECT_FALSE(received.GetIsUnappliedUpdate()); | |
4914 | |
4915 Entry committed(&trans, GET_BY_HANDLE, commit_handle); | |
4916 ASSERT_TRUE(committed.good()); | |
4917 EXPECT_FALSE(committed.GetIsUnsynced()); | |
4918 EXPECT_FALSE(committed.GetIsUnappliedUpdate()); | |
4919 } | |
4920 | |
4921 // Same as above, but this time we fail to download updates. | |
4922 // We should not attempt to commit anything unless we successfully downloaded | |
4923 // updates, otherwise we risk causing a server-side conflict. | |
4924 TEST_F(SyncerTest, UpdateFailsThenDontCommit) { | |
4925 syncable::Id to_receive = ids_.NewServerId(); | |
4926 syncable::Id to_commit = ids_.NewLocalId(); | |
4927 | |
4928 mock_server_->AddUpdateDirectory(to_receive, ids_.root(), "x", 1, 10, | |
4929 foreign_cache_guid(), "-1"); | |
4930 int64_t commit_handle = CreateUnsyncedDirectory("y", to_commit); | |
4931 mock_server_->FailNextPostBufferToPathCall(); | |
4932 EXPECT_FALSE(SyncShareNudge()); | |
4933 | |
4934 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4935 | |
4936 // We did not receive this update. | |
4937 Entry received(&trans, GET_BY_ID, to_receive); | |
4938 ASSERT_FALSE(received.good()); | |
4939 | |
4940 // And our local update remains unapplied. | |
4941 Entry committed(&trans, GET_BY_HANDLE, commit_handle); | |
4942 ASSERT_TRUE(committed.good()); | |
4943 EXPECT_TRUE(committed.GetIsUnsynced()); | |
4944 EXPECT_FALSE(committed.GetIsUnappliedUpdate()); | |
4945 | |
4946 // Inform the Mock we won't be fetching all updates. | |
4947 mock_server_->ClearUpdatesQueue(); | |
4948 } | |
4949 | |
4950 // Downloads two updates and applies them successfully. | |
4951 // This is the "happy path" alternative to ConfigureFailsDontApplyUpdates. | |
4952 TEST_F(SyncerTest, ConfigureDownloadsTwoBatchesSuccess) { | |
4953 syncable::Id node1 = ids_.NewServerId(); | |
4954 syncable::Id node2 = ids_.NewServerId(); | |
4955 | |
4956 // Construct the first GetUpdates response. | |
4957 mock_server_->AddUpdatePref(node1.GetServerId(), "", "one", 1, 10); | |
4958 mock_server_->SetChangesRemaining(1); | |
4959 mock_server_->NextUpdateBatch(); | |
4960 | |
4961 // Construct the second GetUpdates response. | |
4962 mock_server_->AddUpdatePref(node2.GetServerId(), "", "two", 2, 20); | |
4963 | |
4964 SyncShareConfigure(); | |
4965 | |
4966 // The type should now be marked as having the initial sync completed. | |
4967 EXPECT_TRUE(directory()->InitialSyncEndedForType(PREFERENCES)); | |
4968 | |
4969 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
4970 // Both nodes should be downloaded and applied. | |
4971 | |
4972 Entry n1(&trans, GET_BY_ID, node1); | |
4973 ASSERT_TRUE(n1.good()); | |
4974 EXPECT_FALSE(n1.GetIsUnappliedUpdate()); | |
4975 | |
4976 Entry n2(&trans, GET_BY_ID, node2); | |
4977 ASSERT_TRUE(n2.good()); | |
4978 EXPECT_FALSE(n2.GetIsUnappliedUpdate()); | |
4979 } | |
4980 | |
4981 // Same as the above case, but this time the second batch fails to download. | |
4982 TEST_F(SyncerTest, ConfigureFailsDontApplyUpdates) { | |
4983 syncable::Id node1 = ids_.NewServerId(); | |
4984 syncable::Id node2 = ids_.NewServerId(); | |
4985 | |
4986 // The scenario: we have two batches of updates with one update each. A | |
4987 // normal confgure step would download all the updates one batch at a time and | |
4988 // apply them. This configure will succeed in downloading the first batch | |
4989 // then fail when downloading the second. | |
4990 mock_server_->FailNthPostBufferToPathCall(2); | |
4991 | |
4992 // Construct the first GetUpdates response. | |
4993 mock_server_->AddUpdatePref(node1.GetServerId(), "", "one", 1, 10); | |
4994 mock_server_->SetChangesRemaining(1); | |
4995 mock_server_->NextUpdateBatch(); | |
4996 | |
4997 // Construct the second GetUpdates response. | |
4998 mock_server_->AddUpdatePref(node2.GetServerId(), "", "two", 2, 20); | |
4999 | |
5000 SyncShareConfigure(); | |
5001 | |
5002 // The type shouldn't be marked as having the initial sync completed. | |
5003 EXPECT_FALSE(directory()->InitialSyncEndedForType(PREFERENCES)); | |
5004 | |
5005 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5006 | |
5007 // The first node was downloaded, but not applied. | |
5008 Entry n1(&trans, GET_BY_ID, node1); | |
5009 ASSERT_TRUE(n1.good()); | |
5010 EXPECT_TRUE(n1.GetIsUnappliedUpdate()); | |
5011 | |
5012 // The second node was not downloaded. | |
5013 Entry n2(&trans, GET_BY_ID, node2); | |
5014 EXPECT_FALSE(n2.good()); | |
5015 | |
5016 // One update remains undownloaded. | |
5017 mock_server_->ClearUpdatesQueue(); | |
5018 } | |
5019 | |
5020 TEST_F(SyncerTest, GetKeySuccess) { | |
5021 { | |
5022 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
5023 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans)); | |
5024 } | |
5025 | |
5026 SyncShareConfigure(); | |
5027 | |
5028 EXPECT_EQ(SYNCER_OK, session_->status_controller().last_get_key_result()); | |
5029 { | |
5030 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
5031 EXPECT_FALSE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans)); | |
5032 } | |
5033 } | |
5034 | |
5035 TEST_F(SyncerTest, GetKeyEmpty) { | |
5036 { | |
5037 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
5038 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans)); | |
5039 } | |
5040 | |
5041 mock_server_->SetKeystoreKey(std::string()); | |
5042 SyncShareConfigure(); | |
5043 | |
5044 EXPECT_NE(SYNCER_OK, session_->status_controller().last_get_key_result()); | |
5045 { | |
5046 syncable::ReadTransaction rtrans(FROM_HERE, directory()); | |
5047 EXPECT_TRUE(directory()->GetNigoriHandler()->NeedKeystoreKey(&rtrans)); | |
5048 } | |
5049 } | |
5050 | |
5051 // Trigger an update that contains a progress marker only and verify that | |
5052 // the type's permanent folder is created and the type is marked as having | |
5053 // initial sync complete. | |
5054 TEST_F(SyncerTest, ProgressMarkerOnlyUpdateCreatesRootFolder) { | |
5055 EXPECT_FALSE(directory()->InitialSyncEndedForType(PREFERENCES)); | |
5056 sync_pb::DataTypeProgressMarker* marker = | |
5057 mock_server_->AddUpdateProgressMarker(); | |
5058 marker->set_data_type_id(GetSpecificsFieldNumberFromModelType(PREFERENCES)); | |
5059 marker->set_token("foobar"); | |
5060 | |
5061 SyncShareNudge(); | |
5062 | |
5063 { | |
5064 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5065 syncable::Entry root(&trans, syncable::GET_TYPE_ROOT, PREFERENCES); | |
5066 EXPECT_TRUE(root.good()); | |
5067 } | |
5068 | |
5069 EXPECT_TRUE(directory()->InitialSyncEndedForType(PREFERENCES)); | |
5070 } | |
5071 | |
5072 // Tests specifically related to bookmark (and therefore no client tags) sync | |
5073 // logic. Entities without client tags have custom logic in parts of the code, | |
5074 // and hence are not covered by e.g. the Undeletion tests below. | |
5075 class SyncerBookmarksTest : public SyncerTest { | |
5076 public: | |
5077 SyncerBookmarksTest() : metahandle_(syncable::kInvalidMetaHandle) { | |
5078 } | |
5079 | |
5080 void Create() { | |
5081 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
5082 MutableEntry bookmark( | |
5083 &trans, CREATE, BOOKMARKS, ids_.root(), "clientname"); | |
5084 ASSERT_TRUE(bookmark.good()); | |
5085 bookmark.PutSpecifics(DefaultBookmarkSpecifics()); | |
5086 EXPECT_FALSE(bookmark.GetIsUnappliedUpdate()); | |
5087 EXPECT_FALSE(bookmark.GetId().ServerKnows()); | |
5088 metahandle_ = bookmark.GetMetahandle(); | |
5089 local_id_ = bookmark.GetId(); | |
5090 bookmark.PutIsUnsynced(true); | |
5091 } | |
5092 | |
5093 void Update() { | |
5094 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
5095 MutableEntry bookmark(&trans, GET_BY_ID, local_id_); | |
5096 ASSERT_TRUE(bookmark.good()); | |
5097 bookmark.PutSpecifics(DefaultBookmarkSpecifics()); | |
5098 EXPECT_FALSE(bookmark.GetIsUnappliedUpdate()); | |
5099 bookmark.PutIsUnsynced(true); | |
5100 if (bookmark.GetSyncing()) | |
5101 bookmark.PutDirtySync(true); | |
5102 } | |
5103 | |
5104 void Delete() { | |
5105 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
5106 MutableEntry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5107 ASSERT_TRUE(entry.good()); | |
5108 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5109 // The order of setting IS_UNSYNCED vs IS_DEL matters. See | |
5110 // WriteNode::Tombstone(). | |
5111 entry.PutIsUnsynced(true); | |
5112 if (entry.GetSyncing()) | |
5113 entry.PutDirtySync(true); | |
5114 entry.PutIsDel(true); | |
5115 } | |
5116 | |
5117 void UpdateAndDelete() { | |
5118 Update(); | |
5119 Delete(); | |
5120 } | |
5121 | |
5122 void Undelete() { | |
5123 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
5124 MutableEntry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5125 ASSERT_TRUE(entry.good()); | |
5126 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5127 EXPECT_TRUE(entry.GetIsDel()); | |
5128 entry.PutIsDel(false); | |
5129 entry.PutIsUnsynced(true); | |
5130 if (entry.GetSyncing()) | |
5131 entry.PutDirtySync(true); | |
5132 } | |
5133 | |
5134 int64_t GetMetahandleOfTag() { | |
5135 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5136 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5137 EXPECT_TRUE(entry.good()); | |
5138 if (!entry.good()) { | |
5139 return syncable::kInvalidMetaHandle; | |
5140 } | |
5141 return entry.GetMetahandle(); | |
5142 } | |
5143 | |
5144 Id GetServerId() { | |
5145 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5146 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5147 EXPECT_TRUE(entry.good()); | |
5148 if (!entry.good()) { | |
5149 return Id(); | |
5150 } | |
5151 return entry.GetId(); | |
5152 } | |
5153 | |
5154 void ExpectUnsyncedCreation() { | |
5155 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5156 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5157 | |
5158 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5159 EXPECT_FALSE(entry.GetIsDel()); | |
5160 EXPECT_FALSE(entry.GetServerIsDel()); // Never been committed. | |
5161 EXPECT_LT(entry.GetBaseVersion(), 0); | |
5162 EXPECT_TRUE(entry.GetIsUnsynced()); | |
5163 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5164 } | |
5165 | |
5166 void ExpectUnsyncedUndeletion() { | |
5167 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5168 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5169 | |
5170 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5171 EXPECT_FALSE(entry.GetIsDel()); | |
5172 EXPECT_TRUE(entry.GetServerIsDel()); | |
5173 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5174 EXPECT_TRUE(entry.GetIsUnsynced()); | |
5175 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5176 EXPECT_TRUE(entry.GetId().ServerKnows()); | |
5177 } | |
5178 | |
5179 void ExpectUnsyncedEdit() { | |
5180 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5181 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5182 | |
5183 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5184 EXPECT_FALSE(entry.GetIsDel()); | |
5185 EXPECT_FALSE(entry.GetServerIsDel()); | |
5186 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5187 EXPECT_TRUE(entry.GetIsUnsynced()); | |
5188 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5189 EXPECT_TRUE(entry.GetId().ServerKnows()); | |
5190 } | |
5191 | |
5192 void ExpectUnsyncedDeletion() { | |
5193 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5194 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5195 | |
5196 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5197 EXPECT_TRUE(entry.GetIsDel()); | |
5198 EXPECT_FALSE(entry.GetServerIsDel()); | |
5199 EXPECT_TRUE(entry.GetIsUnsynced()); | |
5200 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5201 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5202 EXPECT_GE(entry.GetServerVersion(), 0); | |
5203 } | |
5204 | |
5205 void ExpectSyncedAndCreated() { | |
5206 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5207 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5208 | |
5209 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5210 EXPECT_FALSE(entry.GetIsDel()); | |
5211 EXPECT_FALSE(entry.GetServerIsDel()); | |
5212 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5213 EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion()); | |
5214 EXPECT_FALSE(entry.GetIsUnsynced()); | |
5215 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5216 } | |
5217 | |
5218 void ExpectSyncedAndDeleted() { | |
5219 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5220 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5221 | |
5222 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5223 EXPECT_TRUE(entry.GetIsDel()); | |
5224 EXPECT_TRUE(entry.GetServerIsDel()); | |
5225 EXPECT_FALSE(entry.GetIsUnsynced()); | |
5226 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5227 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5228 EXPECT_GE(entry.GetServerVersion(), 0); | |
5229 } | |
5230 | |
5231 protected: | |
5232 syncable::Id local_id_; | |
5233 int64_t metahandle_; | |
5234 }; | |
5235 | |
5236 TEST_F(SyncerBookmarksTest, CreateSyncThenDeleteSync) { | |
5237 Create(); | |
5238 ExpectUnsyncedCreation(); | |
5239 EXPECT_TRUE(SyncShareNudge()); | |
5240 ExpectSyncedAndCreated(); | |
5241 Delete(); | |
5242 ExpectUnsyncedDeletion(); | |
5243 EXPECT_TRUE(SyncShareNudge()); | |
5244 ExpectSyncedAndDeleted(); | |
5245 } | |
5246 | |
5247 TEST_F(SyncerBookmarksTest, CreateThenDeleteBeforeSync) { | |
5248 Create(); | |
5249 ExpectUnsyncedCreation(); | |
5250 Delete(); | |
5251 | |
5252 // Deleting before the initial commit should result in not needing to send | |
5253 // the delete to the server. It will still be in an unsynced state, but with | |
5254 // IS_UNSYNCED set to false. | |
5255 { | |
5256 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5257 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5258 | |
5259 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5260 EXPECT_TRUE(entry.GetIsDel()); | |
5261 EXPECT_FALSE(entry.GetServerIsDel()); | |
5262 EXPECT_FALSE(entry.GetIsUnsynced()); | |
5263 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5264 EXPECT_EQ(-1, entry.GetBaseVersion()); | |
5265 EXPECT_EQ(0, entry.GetServerVersion()); | |
5266 } | |
5267 } | |
5268 | |
5269 TEST_F(SyncerBookmarksTest, LocalDeleteRemoteChangeConflict) { | |
5270 Create(); | |
5271 ExpectUnsyncedCreation(); | |
5272 EXPECT_TRUE(SyncShareNudge()); | |
5273 ExpectSyncedAndCreated(); | |
5274 Delete(); | |
5275 ExpectUnsyncedDeletion(); | |
5276 | |
5277 // Trigger a getupdates that modifies the bookmark. The update should be | |
5278 // clobbered by the local delete. | |
5279 mock_server_->AddUpdateBookmark(GetServerId(), Id::GetRoot(), "dummy", 10, 10, | |
5280 local_cache_guid(), local_id_.GetServerId()); | |
5281 | |
5282 EXPECT_TRUE(SyncShareNudge()); | |
5283 ExpectSyncedAndDeleted(); | |
5284 } | |
5285 | |
5286 TEST_F(SyncerBookmarksTest, CreateThenDeleteDuringCommit) { | |
5287 Create(); | |
5288 ExpectUnsyncedCreation(); | |
5289 | |
5290 // In the middle of the initial creation commit, perform a deletion. | |
5291 // This should trigger performing two consecutive commit cycles, resulting | |
5292 // in the bookmark being both deleted and synced. | |
5293 mock_server_->SetMidCommitCallback( | |
5294 base::Bind(&SyncerBookmarksTest::Delete, base::Unretained(this))); | |
5295 | |
5296 EXPECT_TRUE(SyncShareNudge()); | |
5297 ExpectSyncedAndDeleted(); | |
5298 } | |
5299 | |
5300 TEST_F(SyncerBookmarksTest, CreateThenUpdateAndDeleteDuringCommit) { | |
5301 Create(); | |
5302 ExpectUnsyncedCreation(); | |
5303 | |
5304 // In the middle of the initial creation commit, perform an updated followed | |
5305 // by a deletion. This should trigger performing two consecutive commit | |
5306 // cycles, resulting in the bookmark being both deleted and synced. | |
5307 mock_server_->SetMidCommitCallback(base::Bind( | |
5308 &SyncerBookmarksTest::UpdateAndDelete, base::Unretained(this))); | |
5309 | |
5310 EXPECT_TRUE(SyncShareNudge()); | |
5311 ExpectSyncedAndDeleted(); | |
5312 } | |
5313 | |
5314 // Test what happens if a client deletes, then recreates, an object very | |
5315 // quickly. It is possible that the deletion gets sent as a commit, and | |
5316 // the undelete happens during the commit request. The principle here | |
5317 // is that with a single committing client, conflicts should never | |
5318 // be encountered, and a client encountering its past actions during | |
5319 // getupdates should never feed back to override later actions. | |
5320 // | |
5321 // In cases of ordering A-F below, the outcome should be the same. | |
5322 // Exercised by UndeleteDuringCommit: | |
5323 // A. Delete - commit - undelete - commitresponse. | |
5324 // B. Delete - commit - undelete - commitresponse - getupdates. | |
5325 // Exercised by UndeleteBeforeCommit: | |
5326 // C. Delete - undelete - commit - commitresponse. | |
5327 // D. Delete - undelete - commit - commitresponse - getupdates. | |
5328 // Exercised by UndeleteAfterCommit: | |
5329 // E. Delete - commit - commitresponse - undelete - commit | |
5330 // - commitresponse. | |
5331 // F. Delete - commit - commitresponse - undelete - commit - | |
5332 // - commitresponse - getupdates. | |
5333 class SyncerUndeletionTest : public SyncerTest { | |
5334 public: | |
5335 SyncerUndeletionTest() | |
5336 : client_tag_("foobar"), | |
5337 metahandle_(syncable::kInvalidMetaHandle) { | |
5338 } | |
5339 | |
5340 void Create() { | |
5341 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
5342 MutableEntry perm_folder( | |
5343 &trans, CREATE, PREFERENCES, ids_.root(), "clientname"); | |
5344 ASSERT_TRUE(perm_folder.good()); | |
5345 perm_folder.PutUniqueClientTag(client_tag_); | |
5346 perm_folder.PutIsUnsynced(true); | |
5347 if (perm_folder.GetSyncing()) | |
5348 perm_folder.PutDirtySync(true); | |
5349 perm_folder.PutSpecifics(DefaultPreferencesSpecifics()); | |
5350 EXPECT_FALSE(perm_folder.GetIsUnappliedUpdate()); | |
5351 EXPECT_FALSE(perm_folder.GetId().ServerKnows()); | |
5352 metahandle_ = perm_folder.GetMetahandle(); | |
5353 local_id_ = perm_folder.GetId(); | |
5354 } | |
5355 | |
5356 void Delete() { | |
5357 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
5358 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); | |
5359 ASSERT_TRUE(entry.good()); | |
5360 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5361 // The order of setting IS_UNSYNCED vs IS_DEL matters. See | |
5362 // WriteNode::Tombstone(). | |
5363 entry.PutIsUnsynced(true); | |
5364 if (entry.GetSyncing()) | |
5365 entry.PutDirtySync(true); | |
5366 entry.PutIsDel(true); | |
5367 } | |
5368 | |
5369 void Undelete() { | |
5370 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); | |
5371 MutableEntry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); | |
5372 ASSERT_TRUE(entry.good()); | |
5373 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5374 EXPECT_TRUE(entry.GetIsDel()); | |
5375 entry.PutIsDel(false); | |
5376 entry.PutIsUnsynced(true); | |
5377 if (entry.GetSyncing()) | |
5378 entry.PutDirtySync(true); | |
5379 } | |
5380 | |
5381 int64_t GetMetahandleOfTag() { | |
5382 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5383 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); | |
5384 EXPECT_TRUE(entry.good()); | |
5385 if (!entry.good()) { | |
5386 return syncable::kInvalidMetaHandle; | |
5387 } | |
5388 return entry.GetMetahandle(); | |
5389 } | |
5390 | |
5391 void ExpectUnsyncedCreation() { | |
5392 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5393 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); | |
5394 | |
5395 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5396 EXPECT_FALSE(entry.GetIsDel()); | |
5397 EXPECT_FALSE(entry.GetServerIsDel()); // Never been committed. | |
5398 EXPECT_LT(entry.GetBaseVersion(), 0); | |
5399 EXPECT_TRUE(entry.GetIsUnsynced()); | |
5400 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5401 } | |
5402 | |
5403 void ExpectUnsyncedUndeletion() { | |
5404 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5405 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); | |
5406 | |
5407 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5408 EXPECT_FALSE(entry.GetIsDel()); | |
5409 EXPECT_TRUE(entry.GetServerIsDel()); | |
5410 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5411 EXPECT_TRUE(entry.GetIsUnsynced()); | |
5412 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5413 EXPECT_TRUE(entry.GetId().ServerKnows()); | |
5414 } | |
5415 | |
5416 void ExpectUnsyncedEdit() { | |
5417 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5418 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); | |
5419 | |
5420 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5421 EXPECT_FALSE(entry.GetIsDel()); | |
5422 EXPECT_FALSE(entry.GetServerIsDel()); | |
5423 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5424 EXPECT_TRUE(entry.GetIsUnsynced()); | |
5425 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5426 EXPECT_TRUE(entry.GetId().ServerKnows()); | |
5427 } | |
5428 | |
5429 void ExpectUnsyncedDeletion() { | |
5430 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5431 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); | |
5432 | |
5433 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5434 EXPECT_TRUE(entry.GetIsDel()); | |
5435 EXPECT_FALSE(entry.GetServerIsDel()); | |
5436 EXPECT_TRUE(entry.GetIsUnsynced()); | |
5437 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5438 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5439 EXPECT_GE(entry.GetServerVersion(), 0); | |
5440 } | |
5441 | |
5442 void ExpectSyncedAndCreated() { | |
5443 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5444 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); | |
5445 | |
5446 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5447 EXPECT_FALSE(entry.GetIsDel()); | |
5448 EXPECT_FALSE(entry.GetServerIsDel()); | |
5449 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5450 EXPECT_EQ(entry.GetBaseVersion(), entry.GetServerVersion()); | |
5451 EXPECT_FALSE(entry.GetIsUnsynced()); | |
5452 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5453 } | |
5454 | |
5455 void ExpectSyncedAndDeleted() { | |
5456 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5457 Entry entry(&trans, GET_BY_CLIENT_TAG, client_tag_); | |
5458 | |
5459 EXPECT_EQ(metahandle_, entry.GetMetahandle()); | |
5460 EXPECT_TRUE(entry.GetIsDel()); | |
5461 EXPECT_TRUE(entry.GetServerIsDel()); | |
5462 EXPECT_FALSE(entry.GetIsUnsynced()); | |
5463 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5464 EXPECT_GE(entry.GetBaseVersion(), 0); | |
5465 EXPECT_GE(entry.GetServerVersion(), 0); | |
5466 } | |
5467 | |
5468 protected: | |
5469 const std::string client_tag_; | |
5470 syncable::Id local_id_; | |
5471 int64_t metahandle_; | |
5472 }; | |
5473 | |
5474 TEST_F(SyncerUndeletionTest, UndeleteDuringCommit) { | |
5475 Create(); | |
5476 ExpectUnsyncedCreation(); | |
5477 EXPECT_TRUE(SyncShareNudge()); | |
5478 | |
5479 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5480 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5481 ExpectSyncedAndCreated(); | |
5482 | |
5483 // Delete, begin committing the delete, then undelete while committing. | |
5484 Delete(); | |
5485 ExpectUnsyncedDeletion(); | |
5486 mock_server_->SetMidCommitCallback( | |
5487 base::Bind(&SyncerUndeletionTest::Undelete, base::Unretained(this))); | |
5488 EXPECT_TRUE(SyncShareNudge()); | |
5489 | |
5490 // We will continue to commit until all nodes are synced, so we expect | |
5491 // that both the delete and following undelete were committed. We haven't | |
5492 // downloaded any updates, though, so the SERVER fields will be the same | |
5493 // as they were at the start of the cycle. | |
5494 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5495 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5496 | |
5497 { | |
5498 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5499 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5500 | |
5501 // Server fields lag behind. | |
5502 EXPECT_FALSE(entry.GetServerIsDel()); | |
5503 | |
5504 // We have committed the second (undelete) update. | |
5505 EXPECT_FALSE(entry.GetIsDel()); | |
5506 EXPECT_FALSE(entry.GetIsUnsynced()); | |
5507 EXPECT_FALSE(entry.GetIsUnappliedUpdate()); | |
5508 } | |
5509 | |
5510 // Now, encounter a GetUpdates corresponding to the deletion from | |
5511 // the server. The undeletion should prevail again and be committed. | |
5512 // None of this should trigger any conflict detection -- it is perfectly | |
5513 // normal to recieve updates from our own commits. | |
5514 mock_server_->SetMidCommitCallback(base::Closure()); | |
5515 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); | |
5516 update->set_originator_cache_guid(local_cache_guid()); | |
5517 update->set_originator_client_item_id(local_id_.GetServerId()); | |
5518 | |
5519 EXPECT_TRUE(SyncShareNudge()); | |
5520 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5521 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5522 ExpectSyncedAndCreated(); | |
5523 } | |
5524 | |
5525 TEST_F(SyncerUndeletionTest, UndeleteBeforeCommit) { | |
5526 Create(); | |
5527 ExpectUnsyncedCreation(); | |
5528 EXPECT_TRUE(SyncShareNudge()); | |
5529 | |
5530 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5531 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5532 ExpectSyncedAndCreated(); | |
5533 | |
5534 // Delete and undelete, then sync to pick up the result. | |
5535 Delete(); | |
5536 ExpectUnsyncedDeletion(); | |
5537 Undelete(); | |
5538 ExpectUnsyncedEdit(); // Edit, not undelete: server thinks it exists. | |
5539 EXPECT_TRUE(SyncShareNudge()); | |
5540 | |
5541 // The item ought to have committed successfully. | |
5542 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5543 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5544 ExpectSyncedAndCreated(); | |
5545 { | |
5546 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5547 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5548 EXPECT_EQ(2, entry.GetBaseVersion()); | |
5549 } | |
5550 | |
5551 // Now, encounter a GetUpdates corresponding to the just-committed | |
5552 // update. | |
5553 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); | |
5554 update->set_originator_cache_guid(local_cache_guid()); | |
5555 update->set_originator_client_item_id(local_id_.GetServerId()); | |
5556 EXPECT_TRUE(SyncShareNudge()); | |
5557 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5558 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5559 ExpectSyncedAndCreated(); | |
5560 } | |
5561 | |
5562 TEST_F(SyncerUndeletionTest, UndeleteAfterCommitButBeforeGetUpdates) { | |
5563 Create(); | |
5564 ExpectUnsyncedCreation(); | |
5565 EXPECT_TRUE(SyncShareNudge()); | |
5566 | |
5567 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5568 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5569 ExpectSyncedAndCreated(); | |
5570 | |
5571 // Delete and commit. | |
5572 Delete(); | |
5573 ExpectUnsyncedDeletion(); | |
5574 EXPECT_TRUE(SyncShareNudge()); | |
5575 | |
5576 // The item ought to have committed successfully. | |
5577 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5578 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5579 ExpectSyncedAndDeleted(); | |
5580 | |
5581 // Before the GetUpdates, the item is locally undeleted. | |
5582 Undelete(); | |
5583 ExpectUnsyncedUndeletion(); | |
5584 | |
5585 // Now, encounter a GetUpdates corresponding to the just-committed | |
5586 // deletion update. The undeletion should prevail. | |
5587 mock_server_->AddUpdateFromLastCommit(); | |
5588 EXPECT_TRUE(SyncShareNudge()); | |
5589 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5590 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5591 ExpectSyncedAndCreated(); | |
5592 } | |
5593 | |
5594 TEST_F(SyncerUndeletionTest, UndeleteAfterDeleteAndGetUpdates) { | |
5595 Create(); | |
5596 ExpectUnsyncedCreation(); | |
5597 EXPECT_TRUE(SyncShareNudge()); | |
5598 | |
5599 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5600 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5601 ExpectSyncedAndCreated(); | |
5602 | |
5603 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); | |
5604 update->set_originator_cache_guid(local_cache_guid()); | |
5605 update->set_originator_client_item_id(local_id_.GetServerId()); | |
5606 EXPECT_TRUE(SyncShareNudge()); | |
5607 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5608 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5609 ExpectSyncedAndCreated(); | |
5610 | |
5611 // Delete and commit. | |
5612 Delete(); | |
5613 ExpectUnsyncedDeletion(); | |
5614 EXPECT_TRUE(SyncShareNudge()); | |
5615 | |
5616 // The item ought to have committed successfully. | |
5617 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5618 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5619 ExpectSyncedAndDeleted(); | |
5620 | |
5621 // Now, encounter a GetUpdates corresponding to the just-committed | |
5622 // deletion update. Should be consistent. | |
5623 mock_server_->AddUpdateFromLastCommit(); | |
5624 EXPECT_TRUE(SyncShareNudge()); | |
5625 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5626 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5627 ExpectSyncedAndDeleted(); | |
5628 | |
5629 // After the GetUpdates, the item is locally undeleted. | |
5630 Undelete(); | |
5631 ExpectUnsyncedUndeletion(); | |
5632 | |
5633 // Now, encounter a GetUpdates corresponding to the just-committed | |
5634 // deletion update. The undeletion should prevail. | |
5635 EXPECT_TRUE(SyncShareNudge()); | |
5636 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5637 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5638 ExpectSyncedAndCreated(); | |
5639 } | |
5640 | |
5641 // Test processing of undeletion GetUpdateses. | |
5642 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletes) { | |
5643 Create(); | |
5644 ExpectUnsyncedCreation(); | |
5645 EXPECT_TRUE(SyncShareNudge()); | |
5646 | |
5647 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5648 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5649 ExpectSyncedAndCreated(); | |
5650 | |
5651 // Add a delete from the server. | |
5652 sync_pb::SyncEntity* update1 = mock_server_->AddUpdateFromLastCommit(); | |
5653 update1->set_originator_cache_guid(local_cache_guid()); | |
5654 update1->set_originator_client_item_id(local_id_.GetServerId()); | |
5655 EXPECT_TRUE(SyncShareNudge()); | |
5656 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5657 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5658 ExpectSyncedAndCreated(); | |
5659 | |
5660 // Some other client deletes the item. | |
5661 { | |
5662 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5663 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5664 mock_server_->AddUpdateTombstone(entry.GetId(), PREFERENCES); | |
5665 } | |
5666 EXPECT_TRUE(SyncShareNudge()); | |
5667 | |
5668 // The update ought to have applied successfully. | |
5669 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5670 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5671 ExpectSyncedAndDeleted(); | |
5672 | |
5673 // Undelete it locally. | |
5674 Undelete(); | |
5675 ExpectUnsyncedUndeletion(); | |
5676 EXPECT_TRUE(SyncShareNudge()); | |
5677 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5678 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5679 ExpectSyncedAndCreated(); | |
5680 | |
5681 // Now, encounter a GetUpdates corresponding to the just-committed | |
5682 // deletion update. The undeletion should prevail. | |
5683 sync_pb::SyncEntity* update2 = mock_server_->AddUpdateFromLastCommit(); | |
5684 update2->set_originator_cache_guid(local_cache_guid()); | |
5685 update2->set_originator_client_item_id(local_id_.GetServerId()); | |
5686 EXPECT_TRUE(SyncShareNudge()); | |
5687 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5688 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5689 ExpectSyncedAndCreated(); | |
5690 } | |
5691 | |
5692 TEST_F(SyncerUndeletionTest, UndeleteAfterOtherClientDeletesImmediately) { | |
5693 Create(); | |
5694 ExpectUnsyncedCreation(); | |
5695 EXPECT_TRUE(SyncShareNudge()); | |
5696 | |
5697 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5698 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5699 ExpectSyncedAndCreated(); | |
5700 | |
5701 // Some other client deletes the item before we get a chance | |
5702 // to GetUpdates our original request. | |
5703 { | |
5704 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5705 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5706 mock_server_->AddUpdateTombstone(entry.GetId(), PREFERENCES); | |
5707 } | |
5708 EXPECT_TRUE(SyncShareNudge()); | |
5709 | |
5710 // The update ought to have applied successfully. | |
5711 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5712 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5713 ExpectSyncedAndDeleted(); | |
5714 | |
5715 // Undelete it locally. | |
5716 Undelete(); | |
5717 ExpectUnsyncedUndeletion(); | |
5718 EXPECT_TRUE(SyncShareNudge()); | |
5719 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5720 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5721 ExpectSyncedAndCreated(); | |
5722 | |
5723 // Now, encounter a GetUpdates corresponding to the just-committed | |
5724 // deletion update. The undeletion should prevail. | |
5725 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); | |
5726 update->set_originator_cache_guid(local_cache_guid()); | |
5727 update->set_originator_client_item_id(local_id_.GetServerId()); | |
5728 EXPECT_TRUE(SyncShareNudge()); | |
5729 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5730 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5731 ExpectSyncedAndCreated(); | |
5732 } | |
5733 | |
5734 TEST_F(SyncerUndeletionTest, OtherClientUndeletes) { | |
5735 Create(); | |
5736 ExpectUnsyncedCreation(); | |
5737 EXPECT_TRUE(SyncShareNudge()); | |
5738 | |
5739 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5740 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5741 ExpectSyncedAndCreated(); | |
5742 | |
5743 // Get the updates of our just-committed entry. | |
5744 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); | |
5745 update->set_originator_cache_guid(local_cache_guid()); | |
5746 update->set_originator_client_item_id(local_id_.GetServerId()); | |
5747 EXPECT_TRUE(SyncShareNudge()); | |
5748 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5749 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5750 ExpectSyncedAndCreated(); | |
5751 | |
5752 // We delete the item. | |
5753 Delete(); | |
5754 ExpectUnsyncedDeletion(); | |
5755 EXPECT_TRUE(SyncShareNudge()); | |
5756 | |
5757 // The update ought to have applied successfully. | |
5758 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5759 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5760 ExpectSyncedAndDeleted(); | |
5761 | |
5762 // Now, encounter a GetUpdates corresponding to the just-committed | |
5763 // deletion update. | |
5764 mock_server_->AddUpdateFromLastCommit(); | |
5765 EXPECT_TRUE(SyncShareNudge()); | |
5766 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5767 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5768 ExpectSyncedAndDeleted(); | |
5769 | |
5770 // Some other client undeletes the item. | |
5771 { | |
5772 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5773 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5774 mock_server_->AddUpdatePref( | |
5775 entry.GetId().GetServerId(), | |
5776 entry.GetParentId().GetServerId(), | |
5777 client_tag_, 100, 1000); | |
5778 } | |
5779 mock_server_->SetLastUpdateClientTag(client_tag_); | |
5780 EXPECT_TRUE(SyncShareNudge()); | |
5781 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5782 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5783 ExpectSyncedAndCreated(); | |
5784 } | |
5785 | |
5786 TEST_F(SyncerUndeletionTest, OtherClientUndeletesImmediately) { | |
5787 Create(); | |
5788 ExpectUnsyncedCreation(); | |
5789 EXPECT_TRUE(SyncShareNudge()); | |
5790 | |
5791 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5792 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5793 ExpectSyncedAndCreated(); | |
5794 | |
5795 // Get the updates of our just-committed entry. | |
5796 sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit(); | |
5797 update->set_originator_cache_guid(local_cache_guid()); | |
5798 { | |
5799 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5800 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5801 update->set_originator_client_item_id(local_id_.GetServerId()); | |
5802 } | |
5803 EXPECT_TRUE(SyncShareNudge()); | |
5804 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5805 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5806 ExpectSyncedAndCreated(); | |
5807 | |
5808 // We delete the item. | |
5809 Delete(); | |
5810 ExpectUnsyncedDeletion(); | |
5811 EXPECT_TRUE(SyncShareNudge()); | |
5812 | |
5813 // The update ought to have applied successfully. | |
5814 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5815 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5816 ExpectSyncedAndDeleted(); | |
5817 | |
5818 // Some other client undeletes before we see the update from our | |
5819 // commit. | |
5820 { | |
5821 syncable::ReadTransaction trans(FROM_HERE, directory()); | |
5822 Entry entry(&trans, GET_BY_HANDLE, metahandle_); | |
5823 mock_server_->AddUpdatePref( | |
5824 entry.GetId().GetServerId(), | |
5825 entry.GetParentId().GetServerId(), | |
5826 client_tag_, 100, 1000); | |
5827 } | |
5828 mock_server_->SetLastUpdateClientTag(client_tag_); | |
5829 EXPECT_TRUE(SyncShareNudge()); | |
5830 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | |
5831 EXPECT_EQ(1, mock_server_->GetAndClearNumGetUpdatesRequests()); | |
5832 ExpectSyncedAndCreated(); | |
5833 } | |
5834 | |
5835 enum { | |
5836 TEST_PARAM_BOOKMARK_ENABLE_BIT, | |
5837 TEST_PARAM_AUTOFILL_ENABLE_BIT, | |
5838 TEST_PARAM_BIT_COUNT | |
5839 }; | |
5840 | |
5841 class MixedResult : | |
5842 public SyncerTest, | |
5843 public ::testing::WithParamInterface<int> { | |
5844 protected: | |
5845 bool ShouldFailBookmarkCommit() { | |
5846 return (GetParam() & (1 << TEST_PARAM_BOOKMARK_ENABLE_BIT)) == 0; | |
5847 } | |
5848 bool ShouldFailAutofillCommit() { | |
5849 return (GetParam() & (1 << TEST_PARAM_AUTOFILL_ENABLE_BIT)) == 0; | |
5850 } | |
5851 }; | |
5852 | |
5853 INSTANTIATE_TEST_CASE_P(ExtensionsActivity, | |
5854 MixedResult, | |
5855 testing::Range(0, 1 << TEST_PARAM_BIT_COUNT)); | |
5856 | |
5857 TEST_P(MixedResult, ExtensionsActivity) { | |
5858 { | |
5859 WriteTransaction wtrans(FROM_HERE, UNITTEST, directory()); | |
5860 | |
5861 MutableEntry pref(&wtrans, CREATE, PREFERENCES, wtrans.root_id(), "pref"); | |
5862 ASSERT_TRUE(pref.good()); | |
5863 pref.PutIsUnsynced(true); | |
5864 | |
5865 MutableEntry bookmark( | |
5866 &wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bookmark"); | |
5867 ASSERT_TRUE(bookmark.good()); | |
5868 bookmark.PutIsUnsynced(true); | |
5869 | |
5870 if (ShouldFailBookmarkCommit()) { | |
5871 mock_server_->SetTransientErrorId(bookmark.GetId()); | |
5872 } | |
5873 | |
5874 if (ShouldFailAutofillCommit()) { | |
5875 mock_server_->SetTransientErrorId(pref.GetId()); | |
5876 } | |
5877 } | |
5878 | |
5879 // Put some extenions activity records into the monitor. | |
5880 { | |
5881 ExtensionsActivity::Records records; | |
5882 records["ABC"].extension_id = "ABC"; | |
5883 records["ABC"].bookmark_write_count = 2049U; | |
5884 records["xyz"].extension_id = "xyz"; | |
5885 records["xyz"].bookmark_write_count = 4U; | |
5886 context_->extensions_activity()->PutRecords(records); | |
5887 } | |
5888 | |
5889 EXPECT_EQ(!ShouldFailBookmarkCommit() && !ShouldFailAutofillCommit(), | |
5890 SyncShareNudge()); | |
5891 | |
5892 ExtensionsActivity::Records final_monitor_records; | |
5893 context_->extensions_activity()->GetAndClearRecords(&final_monitor_records); | |
5894 if (ShouldFailBookmarkCommit()) { | |
5895 ASSERT_EQ(2U, final_monitor_records.size()) | |
5896 << "Should restore records after unsuccessful bookmark commit."; | |
5897 EXPECT_EQ("ABC", final_monitor_records["ABC"].extension_id); | |
5898 EXPECT_EQ("xyz", final_monitor_records["xyz"].extension_id); | |
5899 EXPECT_EQ(2049U, final_monitor_records["ABC"].bookmark_write_count); | |
5900 EXPECT_EQ(4U, final_monitor_records["xyz"].bookmark_write_count); | |
5901 } else { | |
5902 EXPECT_TRUE(final_monitor_records.empty()) | |
5903 << "Should not restore records after successful bookmark commit."; | |
5904 } | |
5905 } | |
5906 | |
5907 } // namespace syncer | |
OLD | NEW |