Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: sync/engine/directory_update_handler_unittest.cc

Issue 2130453004: [Sync] Move //sync to //components/sync. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sync/engine/directory_update_handler.cc ('k') | sync/engine/get_commit_ids.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 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 #include "sync/engine/directory_update_handler.h"
6
7 #include <stdint.h>
8
9 #include <memory>
10 #include <set>
11 #include <string>
12 #include <utility>
13
14 #include "base/compiler_specific.h"
15 #include "base/macros.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/message_loop/message_loop.h"
18 #include "sync/engine/syncer_proto_util.h"
19 #include "sync/internal_api/public/base/attachment_id_proto.h"
20 #include "sync/internal_api/public/base/model_type.h"
21 #include "sync/internal_api/public/test/test_entry_factory.h"
22 #include "sync/protocol/sync.pb.h"
23 #include "sync/sessions/directory_type_debug_info_emitter.h"
24 #include "sync/sessions/status_controller.h"
25 #include "sync/syncable/directory.h"
26 #include "sync/syncable/entry.h"
27 #include "sync/syncable/mutable_entry.h"
28 #include "sync/syncable/syncable_model_neutral_write_transaction.h"
29 #include "sync/syncable/syncable_proto_util.h"
30 #include "sync/syncable/syncable_read_transaction.h"
31 #include "sync/syncable/syncable_write_transaction.h"
32 #include "sync/test/engine/fake_model_worker.h"
33 #include "sync/test/engine/test_directory_setter_upper.h"
34 #include "sync/test/engine/test_id_factory.h"
35 #include "sync/test/engine/test_syncable_utils.h"
36 #include "testing/gtest/include/gtest/gtest.h"
37
38 namespace syncer {
39
40 using syncable::Id;
41 using syncable::UNITTEST;
42
43 static const int64_t kDefaultVersion = 1000;
44
45 // A test harness for tests that focus on processing updates.
46 //
47 // Update processing is what occurs when we first download updates. It converts
48 // the received protobuf message into information in the syncable::Directory.
49 // Any invalid or redundant updates will be dropped at this point.
50 class DirectoryUpdateHandlerProcessUpdateTest : public ::testing::Test {
51 public:
52 DirectoryUpdateHandlerProcessUpdateTest()
53 : ui_worker_(new FakeModelWorker(GROUP_UI)) {
54 }
55
56 ~DirectoryUpdateHandlerProcessUpdateTest() override {}
57
58 void SetUp() override { dir_maker_.SetUp(); }
59
60 void TearDown() override { dir_maker_.TearDown(); }
61
62 syncable::Directory* dir() {
63 return dir_maker_.directory();
64 }
65
66 protected:
67 std::unique_ptr<sync_pb::SyncEntity> CreateUpdate(const std::string& id,
68 const std::string& parent,
69 const ModelType& type);
70
71 // This exists mostly to give tests access to the protected member function.
72 // Warning: This takes the syncable directory lock.
73 void UpdateSyncEntities(
74 DirectoryUpdateHandler* handler,
75 const SyncEntityList& applicable_updates,
76 sessions::StatusController* status);
77
78 // Another function to access private member functions.
79 void UpdateProgressMarkers(
80 DirectoryUpdateHandler* handler,
81 const sync_pb::DataTypeProgressMarker& progress);
82
83 scoped_refptr<FakeModelWorker> ui_worker() {
84 return ui_worker_;
85 }
86
87 bool EntryExists(const std::string& id) {
88 syncable::ReadTransaction trans(FROM_HERE, dir());
89 syncable::Entry e(&trans, syncable::GET_BY_ID,
90 Id::CreateFromServerId(id));
91 return e.good() && !e.GetIsDel();
92 }
93
94 bool TypeRootExists(ModelType model_type) {
95 return dir()->InitialSyncEndedForType(model_type);
96 }
97
98 protected:
99 // Used in the construction of DirectoryTypeDebugInfoEmitters.
100 base::ObserverList<TypeDebugInfoObserver> type_observers_;
101
102 private:
103 base::MessageLoop loop_; // Needed to initialize the directory.
104 TestDirectorySetterUpper dir_maker_;
105 scoped_refptr<FakeModelWorker> ui_worker_;
106 };
107
108 std::unique_ptr<sync_pb::SyncEntity>
109 DirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(const std::string& id,
110 const std::string& parent,
111 const ModelType& type) {
112 std::unique_ptr<sync_pb::SyncEntity> e(new sync_pb::SyncEntity());
113 e->set_id_string(id);
114 e->set_parent_id_string(parent);
115 e->set_non_unique_name(id);
116 e->set_name(id);
117 e->set_version(kDefaultVersion);
118 AddDefaultFieldValue(type, e->mutable_specifics());
119 return e;
120 }
121
122 void DirectoryUpdateHandlerProcessUpdateTest::UpdateSyncEntities(
123 DirectoryUpdateHandler* handler,
124 const SyncEntityList& applicable_updates,
125 sessions::StatusController* status) {
126 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, UNITTEST, dir());
127 handler->UpdateSyncEntities(&trans, applicable_updates, status);
128 }
129
130 void DirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
131 DirectoryUpdateHandler* handler,
132 const sync_pb::DataTypeProgressMarker& progress) {
133 handler->UpdateProgressMarker(progress);
134 }
135
136 static const char kCacheGuid[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
137
138 // Test that the bookmark tag is set on newly downloaded items.
139 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, NewBookmarkTag) {
140 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
141 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
142 sync_pb::GetUpdatesResponse gu_response;
143 sessions::StatusController status;
144
145 // Add a bookmark item to the update message.
146 std::string root = Id::GetRoot().GetServerId();
147 Id server_id = Id::CreateFromServerId("b1");
148 std::unique_ptr<sync_pb::SyncEntity> e =
149 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
150 e->set_originator_cache_guid(
151 std::string(kCacheGuid, arraysize(kCacheGuid)-1));
152 Id client_id = Id::CreateFromClientString("-2");
153 e->set_originator_client_item_id(client_id.GetServerId());
154 e->set_position_in_parent(0);
155
156 // Add it to the applicable updates list.
157 SyncEntityList bookmark_updates;
158 bookmark_updates.push_back(e.get());
159
160 // Process the update.
161 UpdateSyncEntities(&handler, bookmark_updates, &status);
162
163 syncable::ReadTransaction trans(FROM_HERE, dir());
164 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
165 ASSERT_TRUE(entry.good());
166 EXPECT_TRUE(UniquePosition::IsValidSuffix(entry.GetUniqueBookmarkTag()));
167 EXPECT_TRUE(entry.GetServerUniquePosition().IsValid());
168
169 // If this assertion fails, that might indicate that the algorithm used to
170 // generate bookmark tags has been modified. This could have implications for
171 // bookmark ordering. Please make sure you know what you're doing if you
172 // intend to make such a change.
173 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry.GetUniqueBookmarkTag());
174 }
175
176 // Test the receipt of a type root node.
177 TEST_F(DirectoryUpdateHandlerProcessUpdateTest,
178 ReceiveServerCreatedBookmarkFolders) {
179 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
180 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
181 sync_pb::GetUpdatesResponse gu_response;
182 sessions::StatusController status;
183
184 // Create an update that mimics the bookmark root.
185 Id server_id = Id::CreateFromServerId("xyz");
186 std::string root = Id::GetRoot().GetServerId();
187 std::unique_ptr<sync_pb::SyncEntity> e =
188 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
189 e->set_server_defined_unique_tag("google_chrome_bookmarks");
190 e->set_folder(true);
191
192 // Add it to the applicable updates list.
193 SyncEntityList bookmark_updates;
194 bookmark_updates.push_back(e.get());
195
196 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
197
198 // Process it.
199 UpdateSyncEntities(&handler, bookmark_updates, &status);
200
201 // Verify the results.
202 syncable::ReadTransaction trans(FROM_HERE, dir());
203 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
204 ASSERT_TRUE(entry.good());
205
206 EXPECT_FALSE(entry.ShouldMaintainPosition());
207 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
208 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
209 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
210 }
211
212 // Test the receipt of a non-bookmark item.
213 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ReceiveNonBookmarkItem) {
214 DirectoryTypeDebugInfoEmitter emitter(AUTOFILL, &type_observers_);
215 DirectoryUpdateHandler handler(dir(), AUTOFILL, ui_worker(), &emitter);
216 sync_pb::GetUpdatesResponse gu_response;
217 sessions::StatusController status;
218
219 std::string root = Id::GetRoot().GetServerId();
220 Id server_id = Id::CreateFromServerId("xyz");
221 std::unique_ptr<sync_pb::SyncEntity> e =
222 CreateUpdate(SyncableIdToProto(server_id), root, AUTOFILL);
223 e->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I=");
224
225 // Add it to the applicable updates list.
226 SyncEntityList autofill_updates;
227 autofill_updates.push_back(e.get());
228
229 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
230
231 // Process it.
232 UpdateSyncEntities(&handler, autofill_updates, &status);
233
234 syncable::ReadTransaction trans(FROM_HERE, dir());
235 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
236 ASSERT_TRUE(entry.good());
237
238 EXPECT_FALSE(entry.ShouldMaintainPosition());
239 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
240 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
241 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
242 }
243
244 // Tests the setting of progress markers.
245 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ProcessNewProgressMarkers) {
246 DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
247 DirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker(), &emitter);
248
249 sync_pb::DataTypeProgressMarker progress;
250 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS));
251 progress.set_token("token");
252
253 UpdateProgressMarkers(&handler, progress);
254
255 sync_pb::DataTypeProgressMarker saved;
256 dir()->GetDownloadProgress(BOOKMARKS, &saved);
257
258 EXPECT_EQ(progress.token(), saved.token());
259 EXPECT_EQ(progress.data_type_id(), saved.data_type_id());
260 }
261
262 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, GarbageCollectionByVersion) {
263 DirectoryTypeDebugInfoEmitter emitter(SYNCED_NOTIFICATIONS, &type_observers_);
264 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS,
265 ui_worker(), &emitter);
266 sessions::StatusController status;
267
268 sync_pb::DataTypeProgressMarker progress;
269 progress.set_data_type_id(
270 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
271 progress.set_token("token");
272 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
273
274 sync_pb::DataTypeContext context;
275 context.set_data_type_id(
276 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
277 context.set_context("context");
278 context.set_version(1);
279
280 std::unique_ptr<sync_pb::SyncEntity> e1 =
281 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")), "",
282 SYNCED_NOTIFICATIONS);
283
284 std::unique_ptr<sync_pb::SyncEntity> e2 =
285 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e2")), "",
286 SYNCED_NOTIFICATIONS);
287 e2->set_version(kDefaultVersion + 100);
288
289 // Add to the applicable updates list.
290 SyncEntityList updates;
291 updates.push_back(e1.get());
292 updates.push_back(e2.get());
293
294 // Process and apply updates.
295 EXPECT_EQ(
296 SYNCER_OK,
297 handler.ProcessGetUpdatesResponse(progress, context, updates, &status));
298 handler.ApplyUpdates(&status);
299
300 // Verify none is deleted because they are unapplied during GC.
301 EXPECT_TRUE(TypeRootExists(SYNCED_NOTIFICATIONS));
302 EXPECT_TRUE(EntryExists(e1->id_string()));
303 EXPECT_TRUE(EntryExists(e2->id_string()));
304
305 // Process and apply again. Old entry is deleted but not root.
306 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 20);
307 EXPECT_EQ(SYNCER_OK,
308 handler.ProcessGetUpdatesResponse(
309 progress, context, SyncEntityList(), &status));
310 handler.ApplyUpdates(&status);
311 EXPECT_FALSE(EntryExists(e1->id_string()));
312 EXPECT_TRUE(EntryExists(e2->id_string()));
313 }
314
315 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ContextVersion) {
316 DirectoryTypeDebugInfoEmitter emitter(SYNCED_NOTIFICATIONS, &type_observers_);
317 DirectoryUpdateHandler handler(dir(), SYNCED_NOTIFICATIONS,
318 ui_worker(), &emitter);
319 sessions::StatusController status;
320 int field_number = GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS);
321
322 sync_pb::DataTypeProgressMarker progress;
323 progress.set_data_type_id(
324 GetSpecificsFieldNumberFromModelType(SYNCED_NOTIFICATIONS));
325 progress.set_token("token");
326
327 sync_pb::DataTypeContext old_context;
328 old_context.set_version(1);
329 old_context.set_context("data");
330 old_context.set_data_type_id(field_number);
331
332 std::unique_ptr<sync_pb::SyncEntity> e1 =
333 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")), "",
334 SYNCED_NOTIFICATIONS);
335
336 SyncEntityList updates;
337 updates.push_back(e1.get());
338
339 // The first response should be processed fine.
340 EXPECT_EQ(SYNCER_OK,
341 handler.ProcessGetUpdatesResponse(
342 progress, old_context, updates, &status));
343 handler.ApplyUpdates(&status);
344
345 // The PREFERENCES root should be auto-created.
346 EXPECT_TRUE(TypeRootExists(SYNCED_NOTIFICATIONS));
347
348 EXPECT_TRUE(EntryExists(e1->id_string()));
349
350 {
351 sync_pb::DataTypeContext dir_context;
352 syncable::ReadTransaction trans(FROM_HERE, dir());
353 trans.directory()->GetDataTypeContext(
354 &trans, SYNCED_NOTIFICATIONS, &dir_context);
355 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
356 }
357
358 sync_pb::DataTypeContext new_context;
359 new_context.set_version(0);
360 new_context.set_context("old");
361 new_context.set_data_type_id(field_number);
362
363 std::unique_ptr<sync_pb::SyncEntity> e2 =
364 CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e2")), "",
365 SYNCED_NOTIFICATIONS);
366 updates.clear();
367 updates.push_back(e2.get());
368
369 // The second response, with an old context version, should result in an
370 // error and the updates should be dropped.
371 EXPECT_EQ(DATATYPE_TRIGGERED_RETRY,
372 handler.ProcessGetUpdatesResponse(
373 progress, new_context, updates, &status));
374 handler.ApplyUpdates(&status);
375
376 EXPECT_FALSE(EntryExists(e2->id_string()));
377
378 {
379 sync_pb::DataTypeContext dir_context;
380 syncable::ReadTransaction trans(FROM_HERE, dir());
381 trans.directory()->GetDataTypeContext(
382 &trans, SYNCED_NOTIFICATIONS, &dir_context);
383 EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
384 }
385 }
386
387 // See that updates containing attachment metadata are applied
388 // (i.e. server_attachment_metadata is copied to attachment_metadata).
389 TEST_F(DirectoryUpdateHandlerProcessUpdateTest,
390 ProcessAndApplyUpdatesWithAttachments) {
391 DirectoryTypeDebugInfoEmitter emitter(ARTICLES, &type_observers_);
392 DirectoryUpdateHandler handler(dir(), ARTICLES, ui_worker(), &emitter);
393 sessions::StatusController status;
394
395 sync_pb::DataTypeProgressMarker progress;
396 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES));
397 progress.set_token("token");
398 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
399
400 sync_pb::DataTypeContext context;
401 context.set_data_type_id(GetSpecificsFieldNumberFromModelType(ARTICLES));
402 context.set_context("context");
403 context.set_version(1);
404
405 std::unique_ptr<sync_pb::SyncEntity> e1 = CreateUpdate(
406 SyncableIdToProto(Id::CreateFromServerId("e1")), "", ARTICLES);
407 sync_pb::AttachmentIdProto* attachment_id = e1->add_attachment_id();
408 *attachment_id = CreateAttachmentIdProto(0, 0);
409
410 SyncEntityList updates;
411 updates.push_back(e1.get());
412
413 // Process and apply updates.
414 EXPECT_EQ(
415 SYNCER_OK,
416 handler.ProcessGetUpdatesResponse(progress, context, updates, &status));
417 handler.ApplyUpdates(&status);
418
419 ASSERT_TRUE(TypeRootExists(ARTICLES));
420 ASSERT_TRUE(EntryExists(e1->id_string()));
421 {
422 syncable::ReadTransaction trans(FROM_HERE, dir());
423 syncable::Entry e(&trans,
424 syncable::GET_BY_ID,
425 Id::CreateFromServerId(e1->id_string()));
426
427 // See that the attachment_metadata is correct.
428 sync_pb::AttachmentMetadata attachment_metadata = e.GetAttachmentMetadata();
429 ASSERT_EQ(1, attachment_metadata.record_size());
430 ASSERT_EQ(attachment_id->SerializeAsString(),
431 attachment_metadata.record(0).id().SerializeAsString());
432 ASSERT_TRUE(attachment_metadata.record(0).is_on_server());
433
434 // See that attachment_metadata and server_attachment_metadata are equal.
435 ASSERT_EQ(attachment_metadata.SerializeAsString(),
436 e.GetServerAttachmentMetadata().SerializeAsString());
437 }
438 }
439
440 // Tests that IsInitialSyncEnded value is updated by ApplyUpdates, but not by
441 // ProcessGetUpdatesResponse.
442 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, IsInitialSyncEnded) {
443 DirectoryTypeDebugInfoEmitter emitter(AUTOFILL, &type_observers_);
444 DirectoryUpdateHandler handler(dir(), AUTOFILL, ui_worker(), &emitter);
445 sessions::StatusController status;
446
447 EXPECT_FALSE(handler.IsInitialSyncEnded());
448
449 sync_pb::DataTypeProgressMarker progress;
450 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(AUTOFILL));
451 progress.set_token("token");
452 progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
453
454 std::unique_ptr<sync_pb::SyncEntity> e = CreateUpdate(
455 SyncableIdToProto(Id::CreateFromServerId("e1")), "", AUTOFILL);
456
457 SyncEntityList updates;
458 updates.push_back(e.get());
459
460 handler.ProcessGetUpdatesResponse(progress, sync_pb::DataTypeContext(),
461 updates, &status);
462
463 EXPECT_FALSE(handler.IsInitialSyncEnded());
464
465 handler.ApplyUpdates(&status);
466 EXPECT_TRUE(handler.IsInitialSyncEnded());
467 }
468
469 // A test harness for tests that focus on applying updates.
470 //
471 // Update application is performed when we want to take updates that were
472 // previously downloaded, processed, and stored in our syncable::Directory
473 // and use them to update our local state (both the Directory's local state
474 // and the model's local state, though these tests focus only on the Directory's
475 // local state).
476 //
477 // This is kept separate from the update processing test in part for historical
478 // reasons, and in part because these tests may require a bit more infrastrcture
479 // in the future. Update application should happen on a different thread a lot
480 // of the time so these tests may end up requiring more infrastructure than the
481 // update processing tests. Currently, we're bypassing most of those issues by
482 // using FakeModelWorkers, so there's not much difference between the two test
483 // harnesses.
484 class DirectoryUpdateHandlerApplyUpdateTest : public ::testing::Test {
485 public:
486 DirectoryUpdateHandlerApplyUpdateTest()
487 : ui_worker_(new FakeModelWorker(GROUP_UI)),
488 password_worker_(new FakeModelWorker(GROUP_PASSWORD)),
489 passive_worker_(new FakeModelWorker(GROUP_PASSIVE)),
490 bookmarks_emitter_(BOOKMARKS, &type_observers_),
491 passwords_emitter_(PASSWORDS, &type_observers_),
492 articles_emitter_(ARTICLES, &type_observers_) {}
493
494 void SetUp() override {
495 dir_maker_.SetUp();
496 entry_factory_.reset(new TestEntryFactory(directory()));
497
498 update_handler_map_.insert(std::make_pair(
499 BOOKMARKS,
500 base::WrapUnique(new DirectoryUpdateHandler(
501 directory(), BOOKMARKS, ui_worker_, &bookmarks_emitter_))));
502 update_handler_map_.insert(std::make_pair(
503 PASSWORDS,
504 base::WrapUnique(new DirectoryUpdateHandler(
505 directory(), PASSWORDS, password_worker_, &passwords_emitter_))));
506 update_handler_map_.insert(std::make_pair(
507 ARTICLES, base::WrapUnique(new DirectoryUpdateHandler(
508 directory(), ARTICLES, ui_worker_, &articles_emitter_))));
509 }
510
511 void TearDown() override { dir_maker_.TearDown(); }
512
513 const UpdateCounters& GetBookmarksUpdateCounters() {
514 return bookmarks_emitter_.GetUpdateCounters();
515 }
516
517 const UpdateCounters& GetPasswordsUpdateCounters() {
518 return passwords_emitter_.GetUpdateCounters();
519 }
520
521 const UpdateCounters& GetArticlesUpdateCounters() {
522 return articles_emitter_.GetUpdateCounters();
523 }
524
525 protected:
526 void ApplyBookmarkUpdates(sessions::StatusController* status) {
527 update_handler_map_.find(BOOKMARKS)->second->ApplyUpdates(status);
528 }
529
530 void ApplyPasswordUpdates(sessions::StatusController* status) {
531 update_handler_map_.find(PASSWORDS)->second->ApplyUpdates(status);
532 }
533
534 void ApplyArticlesUpdates(sessions::StatusController* status) {
535 update_handler_map_.find(ARTICLES)->second->ApplyUpdates(status);
536 }
537
538 TestEntryFactory* entry_factory() {
539 return entry_factory_.get();
540 }
541
542 syncable::Directory* directory() {
543 return dir_maker_.directory();
544 }
545
546 private:
547 base::MessageLoop loop_; // Needed to initialize the directory.
548 TestDirectorySetterUpper dir_maker_;
549 std::unique_ptr<TestEntryFactory> entry_factory_;
550
551 scoped_refptr<FakeModelWorker> ui_worker_;
552 scoped_refptr<FakeModelWorker> password_worker_;
553 scoped_refptr<FakeModelWorker> passive_worker_;
554
555 base::ObserverList<TypeDebugInfoObserver> type_observers_;
556 DirectoryTypeDebugInfoEmitter bookmarks_emitter_;
557 DirectoryTypeDebugInfoEmitter passwords_emitter_;
558 DirectoryTypeDebugInfoEmitter articles_emitter_;
559
560 std::map<ModelType, std::unique_ptr<UpdateHandler>> update_handler_map_;
561 };
562
563 namespace {
564 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
565 sync_pb::EntitySpecifics result;
566 AddDefaultFieldValue(BOOKMARKS, &result);
567 return result;
568 }
569 } // namespace
570
571 // Test update application for a few bookmark items.
572 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmark) {
573 sessions::StatusController status;
574
575 std::string root_server_id = Id::GetRoot().GetServerId();
576 int64_t parent_handle =
577 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
578 "parent", DefaultBookmarkSpecifics(), root_server_id);
579 int64_t child_handle =
580 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
581 "child", DefaultBookmarkSpecifics(), "parent");
582
583 ApplyBookmarkUpdates(&status);
584
585 const UpdateCounters& counter = GetBookmarksUpdateCounters();
586 EXPECT_EQ(0, counter.num_encryption_conflict_application_failures)
587 << "Simple update shouldn't result in conflicts";
588 EXPECT_EQ(0, counter.num_hierarchy_conflict_application_failures)
589 << "Simple update shouldn't result in conflicts";
590 EXPECT_EQ(2, counter.num_updates_applied)
591 << "All items should have been successfully applied";
592
593 {
594 syncable::ReadTransaction trans(FROM_HERE, directory());
595
596 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
597 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
598
599 ASSERT_TRUE(parent.good());
600 ASSERT_TRUE(child.good());
601
602 EXPECT_FALSE(parent.GetIsUnsynced());
603 EXPECT_FALSE(parent.GetIsUnappliedUpdate());
604 EXPECT_FALSE(child.GetIsUnsynced());
605 EXPECT_FALSE(child.GetIsUnappliedUpdate());
606 }
607 }
608
609 // Test that the applicator can handle updates delivered out of order.
610 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
611 BookmarkChildrenBeforeParent) {
612 // Start with some bookmarks whose parents are unknown.
613 std::string root_server_id = Id::GetRoot().GetServerId();
614 int64_t a_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
615 "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
616 int64_t x_handle = entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
617 "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
618
619 // Update application will fail.
620 sessions::StatusController status1;
621 ApplyBookmarkUpdates(&status1);
622 EXPECT_EQ(0, status1.num_updates_applied());
623 EXPECT_EQ(2, status1.num_hierarchy_conflicts());
624
625 {
626 syncable::ReadTransaction trans(FROM_HERE, directory());
627
628 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
629 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
630
631 ASSERT_TRUE(a.good());
632 ASSERT_TRUE(x.good());
633
634 EXPECT_TRUE(a.GetIsUnappliedUpdate());
635 EXPECT_TRUE(x.GetIsUnappliedUpdate());
636 }
637
638 // Now add their parent and a few siblings.
639 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
640 "parent", DefaultBookmarkSpecifics(), root_server_id);
641 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
642 "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
643 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
644 "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
645
646 // Update application will succeed.
647 sessions::StatusController status2;
648 ApplyBookmarkUpdates(&status2);
649 EXPECT_EQ(5, status2.num_updates_applied())
650 << "All updates should have been successfully applied";
651
652 {
653 syncable::ReadTransaction trans(FROM_HERE, directory());
654
655 syncable::Entry a(&trans, syncable::GET_BY_HANDLE, a_handle);
656 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
657
658 ASSERT_TRUE(a.good());
659 ASSERT_TRUE(x.good());
660
661 EXPECT_FALSE(a.GetIsUnappliedUpdate());
662 EXPECT_FALSE(x.GetIsUnappliedUpdate());
663 }
664 }
665
666 // Try to apply changes on an item that is both IS_UNSYNCED and
667 // IS_UNAPPLIED_UPDATE. Conflict resolution should be performed.
668 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SimpleBookmarkConflict) {
669 int64_t handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
670
671 int original_server_version = -10;
672 {
673 syncable::ReadTransaction trans(FROM_HERE, directory());
674 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
675 original_server_version = e.GetServerVersion();
676 ASSERT_NE(original_server_version, e.GetBaseVersion());
677 EXPECT_TRUE(e.GetIsUnsynced());
678 }
679
680 sessions::StatusController status;
681 ApplyBookmarkUpdates(&status);
682
683 const UpdateCounters& counters = GetBookmarksUpdateCounters();
684 EXPECT_EQ(1, counters.num_server_overwrites)
685 << "Unsynced and unapplied item conflict should be resolved";
686 EXPECT_EQ(0, counters.num_updates_applied)
687 << "Update should not be applied; we should override the server.";
688
689 {
690 syncable::ReadTransaction trans(FROM_HERE, directory());
691 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
692 ASSERT_TRUE(e.good());
693 EXPECT_EQ(original_server_version, e.GetServerVersion());
694 EXPECT_EQ(original_server_version, e.GetBaseVersion());
695 EXPECT_FALSE(e.GetIsUnappliedUpdate());
696
697 // The unsynced flag will remain set until we successfully commit the item.
698 EXPECT_TRUE(e.GetIsUnsynced());
699 }
700 }
701
702 // Create a simple conflict that is also a hierarchy conflict. If we were to
703 // follow the normal "server wins" logic, we'd end up violating hierarchy
704 // constraints. The hierarchy conflict must take precedence. We can not allow
705 // the update to be applied. The item must remain in the conflict state.
706 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, HierarchyAndSimpleConflict) {
707 // Create a simply-conflicting item. It will start with valid parent ids.
708 int64_t handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
709 "orphaned_by_server");
710 {
711 // Manually set the SERVER_PARENT_ID to bad value.
712 // A bad parent indicates a hierarchy conflict.
713 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
714 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
715 ASSERT_TRUE(entry.good());
716
717 entry.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
718 }
719
720 sessions::StatusController status;
721 ApplyBookmarkUpdates(&status);
722
723 const UpdateCounters& counters = GetBookmarksUpdateCounters();
724 EXPECT_EQ(0, counters.num_updates_applied);
725 EXPECT_EQ(0, counters.num_server_overwrites);
726 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
727
728 {
729 syncable::ReadTransaction trans(FROM_HERE, directory());
730 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
731 ASSERT_TRUE(e.good());
732 EXPECT_TRUE(e.GetIsUnappliedUpdate());
733 EXPECT_TRUE(e.GetIsUnsynced());
734 }
735 }
736
737 // Attempt to apply an udpate that would create a bookmark folder loop. This
738 // application should fail.
739 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, BookmarkFolderLoop) {
740 // Item 'X' locally has parent of 'root'. Server is updating it to have
741 // parent of 'Y'.
742
743 // Create it as a child of root node.
744 int64_t handle = entry_factory()->CreateSyncedItem("X", BOOKMARKS, true);
745
746 {
747 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
748 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
749 ASSERT_TRUE(entry.good());
750
751 // Re-parent from root to "Y"
752 entry.PutServerVersion(entry_factory()->GetNextRevision());
753 entry.PutIsUnappliedUpdate(true);
754 entry.PutServerParentId(TestIdFactory::MakeServer("Y"));
755 }
756
757 // Item 'Y' is child of 'X'.
758 entry_factory()->CreateUnsyncedItem(
759 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
760 BOOKMARKS, NULL);
761
762 // If the server's update were applied, we would have X be a child of Y, and Y
763 // as a child of X. That's a directory loop. The UpdateApplicator should
764 // prevent the update from being applied and note that this is a hierarchy
765 // conflict.
766
767 sessions::StatusController status;
768 ApplyBookmarkUpdates(&status);
769
770 // This should count as a hierarchy conflict.
771 const UpdateCounters& counters = GetBookmarksUpdateCounters();
772 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
773
774 {
775 syncable::ReadTransaction trans(FROM_HERE, directory());
776 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
777 ASSERT_TRUE(e.good());
778 EXPECT_TRUE(e.GetIsUnappliedUpdate());
779 EXPECT_FALSE(e.GetIsUnsynced());
780 }
781 }
782
783 // Test update application where the update has been orphaned by a local folder
784 // deletion. The update application attempt should fail.
785 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
786 HierarchyConflictDeletedParent) {
787 // Create a locally deleted parent item.
788 int64_t parent_handle;
789 entry_factory()->CreateUnsyncedItem(
790 Id::CreateFromServerId("parent"), TestIdFactory::root(),
791 "parent", true, BOOKMARKS, &parent_handle);
792 {
793 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
794 syncable::MutableEntry entry(&trans,
795 syncable::GET_BY_HANDLE,
796 parent_handle);
797 entry.PutIsDel(true);
798 }
799
800 // Create an incoming child from the server.
801 int64_t child_handle = entry_factory()->CreateUnappliedNewItemWithParent(
802 "child", DefaultBookmarkSpecifics(), "parent");
803
804 // The server's update may seem valid to some other client, but on this client
805 // that new item's parent no longer exists. The update should not be applied
806 // and the update applicator should indicate this is a hierarchy conflict.
807
808 sessions::StatusController status;
809 ApplyBookmarkUpdates(&status);
810 const UpdateCounters& counters = GetBookmarksUpdateCounters();
811 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
812
813 {
814 syncable::ReadTransaction trans(FROM_HERE, directory());
815 syncable::Entry child(&trans, syncable::GET_BY_HANDLE, child_handle);
816 ASSERT_TRUE(child.good());
817 EXPECT_TRUE(child.GetIsUnappliedUpdate());
818 EXPECT_FALSE(child.GetIsUnsynced());
819 }
820 }
821
822 // Attempt to apply an update that deletes a folder where the folder has
823 // locally-created children. The update application should fail.
824 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
825 HierarchyConflictDeleteNonEmptyDirectory) {
826 // Create a server-deleted folder as a child of root node.
827 int64_t parent_handle =
828 entry_factory()->CreateSyncedItem("parent", BOOKMARKS, true);
829 {
830 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
831 syncable::MutableEntry entry(&trans,
832 syncable::GET_BY_HANDLE,
833 parent_handle);
834 ASSERT_TRUE(entry.good());
835
836 // Delete it on the server.
837 entry.PutServerVersion(entry_factory()->GetNextRevision());
838 entry.PutIsUnappliedUpdate(true);
839 entry.PutServerParentId(TestIdFactory::root());
840 entry.PutServerIsDel(true);
841 }
842
843 // Create a local child of the server-deleted directory.
844 entry_factory()->CreateUnsyncedItem(
845 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
846 "child", false, BOOKMARKS, NULL);
847
848 // The server's request to delete the directory must be ignored, otherwise our
849 // unsynced new child would be orphaned. This is a hierarchy conflict.
850
851 sessions::StatusController status;
852 ApplyBookmarkUpdates(&status);
853
854 // This should count as a hierarchy conflict.
855 const UpdateCounters& counters = GetBookmarksUpdateCounters();
856 EXPECT_EQ(1, counters.num_hierarchy_conflict_application_failures);
857
858 {
859 syncable::ReadTransaction trans(FROM_HERE, directory());
860 syncable::Entry parent(&trans, syncable::GET_BY_HANDLE, parent_handle);
861 ASSERT_TRUE(parent.good());
862 EXPECT_TRUE(parent.GetIsUnappliedUpdate());
863 EXPECT_FALSE(parent.GetIsUnsynced());
864 }
865 }
866
867 // Attempt to apply updates where the updated item's parent is not known to this
868 // client. The update application attempt should fail.
869 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
870 HierarchyConflictUnknownParent) {
871 // We shouldn't be able to do anything with either of these items.
872 int64_t x_handle = entry_factory()->CreateUnappliedNewItemWithParent(
873 "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
874 int64_t y_handle = entry_factory()->CreateUnappliedNewItemWithParent(
875 "some_other_item", DefaultBookmarkSpecifics(), "some_item");
876
877 sessions::StatusController status;
878 ApplyBookmarkUpdates(&status);
879
880 const UpdateCounters& counters = GetBookmarksUpdateCounters();
881 EXPECT_EQ(2, counters.num_hierarchy_conflict_application_failures)
882 << "All updates with an unknown ancestors should be in conflict";
883 EXPECT_EQ(0, counters.num_updates_applied)
884 << "No item with an unknown ancestor should be applied";
885
886 {
887 syncable::ReadTransaction trans(FROM_HERE, directory());
888 syncable::Entry x(&trans, syncable::GET_BY_HANDLE, x_handle);
889 syncable::Entry y(&trans, syncable::GET_BY_HANDLE, y_handle);
890 ASSERT_TRUE(x.good());
891 ASSERT_TRUE(y.good());
892 EXPECT_TRUE(x.GetIsUnappliedUpdate());
893 EXPECT_TRUE(y.GetIsUnappliedUpdate());
894 EXPECT_FALSE(x.GetIsUnsynced());
895 EXPECT_FALSE(y.GetIsUnsynced());
896 }
897 }
898
899 // Attempt application of a mix of items. Some update application attempts will
900 // fail due to hierarchy conflicts. Others should succeed.
901 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, ItemsBothKnownAndUnknown) {
902 // See what happens when there's a mixture of good and bad updates.
903 std::string root_server_id = Id::GetRoot().GetServerId();
904 int64_t u1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
905 "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
906 int64_t k1_handle = entry_factory()->CreateUnappliedNewItemWithParent(
907 "first_known_item", DefaultBookmarkSpecifics(), root_server_id);
908 int64_t u2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
909 "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
910 int64_t k2_handle = entry_factory()->CreateUnappliedNewItemWithParent(
911 "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
912 int64_t k3_handle = entry_factory()->CreateUnappliedNewItemWithParent(
913 "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
914 int64_t k4_handle = entry_factory()->CreateUnappliedNewItemWithParent(
915 "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id);
916
917 sessions::StatusController status;
918 ApplyBookmarkUpdates(&status);
919
920 const UpdateCounters& counters = GetBookmarksUpdateCounters();
921 EXPECT_EQ(2, counters.num_hierarchy_conflict_application_failures)
922 << "The updates with unknown ancestors should be in conflict";
923 EXPECT_EQ(4, counters.num_updates_applied)
924 << "The updates with known ancestors should be successfully applied";
925
926 {
927 syncable::ReadTransaction trans(FROM_HERE, directory());
928 syncable::Entry u1(&trans, syncable::GET_BY_HANDLE, u1_handle);
929 syncable::Entry u2(&trans, syncable::GET_BY_HANDLE, u2_handle);
930 syncable::Entry k1(&trans, syncable::GET_BY_HANDLE, k1_handle);
931 syncable::Entry k2(&trans, syncable::GET_BY_HANDLE, k2_handle);
932 syncable::Entry k3(&trans, syncable::GET_BY_HANDLE, k3_handle);
933 syncable::Entry k4(&trans, syncable::GET_BY_HANDLE, k4_handle);
934 ASSERT_TRUE(u1.good());
935 ASSERT_TRUE(u2.good());
936 ASSERT_TRUE(k1.good());
937 ASSERT_TRUE(k2.good());
938 ASSERT_TRUE(k3.good());
939 ASSERT_TRUE(k4.good());
940 EXPECT_TRUE(u1.GetIsUnappliedUpdate());
941 EXPECT_TRUE(u2.GetIsUnappliedUpdate());
942 EXPECT_FALSE(k1.GetIsUnappliedUpdate());
943 EXPECT_FALSE(k2.GetIsUnappliedUpdate());
944 EXPECT_FALSE(k3.GetIsUnappliedUpdate());
945 EXPECT_FALSE(k4.GetIsUnappliedUpdate());
946 }
947 }
948
949 // Attempt application of password upates where the passphrase is known.
950 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, DecryptablePassword) {
951 // Decryptable password updates should be applied.
952 Cryptographer* cryptographer;
953 {
954 // Storing the cryptographer separately is bad, but for this test we
955 // know it's safe.
956 syncable::ReadTransaction trans(FROM_HERE, directory());
957 cryptographer = directory()->GetCryptographer(&trans);
958 }
959
960 KeyParams params = {"localhost", "dummy", "foobar"};
961 cryptographer->AddKey(params);
962
963 sync_pb::EntitySpecifics specifics;
964 sync_pb::PasswordSpecificsData data;
965 data.set_origin("http://example.com");
966
967 cryptographer->Encrypt(data,
968 specifics.mutable_password()->mutable_encrypted());
969 int64_t handle =
970 entry_factory()->CreateUnappliedNewItem("item", specifics, false);
971
972 sessions::StatusController status;
973 ApplyPasswordUpdates(&status);
974
975 const UpdateCounters& counters = GetPasswordsUpdateCounters();
976 EXPECT_EQ(1, counters.num_updates_applied)
977 << "The updates that can be decrypted should be applied";
978
979 {
980 syncable::ReadTransaction trans(FROM_HERE, directory());
981 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
982 ASSERT_TRUE(e.good());
983 EXPECT_FALSE(e.GetIsUnappliedUpdate());
984 EXPECT_FALSE(e.GetIsUnsynced());
985 }
986 }
987
988 // Attempt application of encrypted items when the passphrase is not known.
989 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, UndecryptableData) {
990 // Undecryptable updates should not be applied.
991 sync_pb::EntitySpecifics encrypted_bookmark;
992 encrypted_bookmark.mutable_encrypted();
993 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
994 std::string root_server_id = Id::GetRoot().GetServerId();
995 int64_t folder_handle = entry_factory()->CreateUnappliedNewItemWithParent(
996 "folder", encrypted_bookmark, root_server_id);
997 int64_t bookmark_handle = entry_factory()->CreateUnappliedNewItem(
998 "item2", encrypted_bookmark, false);
999 sync_pb::EntitySpecifics encrypted_password;
1000 encrypted_password.mutable_password();
1001 int64_t password_handle = entry_factory()->CreateUnappliedNewItem(
1002 "item3", encrypted_password, false);
1003
1004 sessions::StatusController status;
1005 ApplyBookmarkUpdates(&status);
1006 ApplyPasswordUpdates(&status);
1007
1008 const UpdateCounters& bm_counters = GetBookmarksUpdateCounters();
1009 EXPECT_EQ(2, bm_counters.num_encryption_conflict_application_failures)
1010 << "Updates that can't be decrypted should be in encryption conflict";
1011 EXPECT_EQ(0, bm_counters.num_updates_applied)
1012 << "No update that can't be decrypted should be applied";
1013
1014 const UpdateCounters& pw_counters = GetPasswordsUpdateCounters();
1015 EXPECT_EQ(1, pw_counters.num_encryption_conflict_application_failures)
1016 << "Updates that can't be decrypted should be in encryption conflict";
1017 EXPECT_EQ(0, pw_counters.num_updates_applied)
1018 << "No update that can't be decrypted should be applied";
1019
1020 {
1021 syncable::ReadTransaction trans(FROM_HERE, directory());
1022 syncable::Entry folder(&trans, syncable::GET_BY_HANDLE, folder_handle);
1023 syncable::Entry bm(&trans, syncable::GET_BY_HANDLE, bookmark_handle);
1024 syncable::Entry pw(&trans, syncable::GET_BY_HANDLE, password_handle);
1025 ASSERT_TRUE(folder.good());
1026 ASSERT_TRUE(bm.good());
1027 ASSERT_TRUE(pw.good());
1028 EXPECT_TRUE(folder.GetIsUnappliedUpdate());
1029 EXPECT_TRUE(bm.GetIsUnappliedUpdate());
1030 EXPECT_TRUE(pw.GetIsUnappliedUpdate());
1031 }
1032 }
1033
1034 // Test a mix of decryptable and undecryptable updates.
1035 TEST_F(DirectoryUpdateHandlerApplyUpdateTest, SomeUndecryptablePassword) {
1036 Cryptographer* cryptographer;
1037
1038 int64_t decryptable_handle = -1;
1039 int64_t undecryptable_handle = -1;
1040
1041 // Only decryptable password updates should be applied.
1042 {
1043 sync_pb::EntitySpecifics specifics;
1044 sync_pb::PasswordSpecificsData data;
1045 data.set_origin("http://example.com/1");
1046 {
1047 syncable::ReadTransaction trans(FROM_HERE, directory());
1048 cryptographer = directory()->GetCryptographer(&trans);
1049
1050 KeyParams params = {"localhost", "dummy", "foobar"};
1051 cryptographer->AddKey(params);
1052
1053 cryptographer->Encrypt(data,
1054 specifics.mutable_password()->mutable_encrypted());
1055 }
1056 decryptable_handle =
1057 entry_factory()->CreateUnappliedNewItem("item1", specifics, false);
1058 }
1059 {
1060 // Create a new cryptographer, independent of the one in the session.
1061 Cryptographer other_cryptographer(cryptographer->encryptor());
1062 KeyParams params = {"localhost", "dummy", "bazqux"};
1063 other_cryptographer.AddKey(params);
1064
1065 sync_pb::EntitySpecifics specifics;
1066 sync_pb::PasswordSpecificsData data;
1067 data.set_origin("http://example.com/2");
1068
1069 other_cryptographer.Encrypt(data,
1070 specifics.mutable_password()->mutable_encrypted());
1071 undecryptable_handle =
1072 entry_factory()->CreateUnappliedNewItem("item2", specifics, false);
1073 }
1074
1075 sessions::StatusController status;
1076 ApplyPasswordUpdates(&status);
1077
1078 const UpdateCounters& counters = GetPasswordsUpdateCounters();
1079 EXPECT_EQ(1, counters.num_encryption_conflict_application_failures)
1080 << "The updates that can't be decrypted should be in encryption "
1081 << "conflict";
1082 EXPECT_EQ(1, counters.num_updates_applied)
1083 << "The undecryptable password update shouldn't be applied";
1084
1085 {
1086 syncable::ReadTransaction trans(FROM_HERE, directory());
1087 syncable::Entry e1(&trans, syncable::GET_BY_HANDLE, decryptable_handle);
1088 syncable::Entry e2(&trans, syncable::GET_BY_HANDLE, undecryptable_handle);
1089 ASSERT_TRUE(e1.good());
1090 ASSERT_TRUE(e2.good());
1091 EXPECT_FALSE(e1.GetIsUnappliedUpdate());
1092 EXPECT_TRUE(e2.GetIsUnappliedUpdate());
1093 }
1094 }
1095
1096 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
1097 SimpleConflictDifferentAttachmentMetadata) {
1098 const bool is_folder = false;
1099 sync_pb::EntitySpecifics specifics;
1100 *specifics.mutable_article() = sync_pb::ArticleSpecifics();
1101 int64_t handle =
1102 entry_factory()->CreateSyncedItem("art1", ARTICLES, is_folder);
1103
1104 sync_pb::AttachmentIdProto local_attachment_id =
1105 CreateAttachmentIdProto(0, 0);
1106 sync_pb::AttachmentIdProto server_attachment_id =
1107 CreateAttachmentIdProto(0, 0);
1108
1109 // Add an attachment to the local attachment metadata.
1110 sync_pb::AttachmentMetadata local_metadata;
1111 sync_pb::AttachmentMetadataRecord* local_record = local_metadata.add_record();
1112 *local_record->mutable_id() = local_attachment_id;
1113 local_record->set_is_on_server(true);
1114 entry_factory()->SetLocalAttachmentMetadataForItem(handle, local_metadata);
1115
1116 // Add a different attachment to the server attachment metadata.
1117 sync_pb::AttachmentMetadata server_metadata;
1118 sync_pb::AttachmentMetadataRecord* server_record =
1119 server_metadata.add_record();
1120 *server_record->mutable_id() = server_attachment_id;
1121 server_record->set_is_on_server(true);
1122 entry_factory()->SetServerAttachmentMetadataForItem(handle, server_metadata);
1123
1124 // At this point we have a simple conflict. The server says art1 should have
1125 // server_attachment_id, but the local sync engine says it should have
1126 // local_attachment_id.
1127
1128 sessions::StatusController status;
1129 ApplyArticlesUpdates(&status);
1130
1131 // See that the server won.
1132 const UpdateCounters& counters = GetArticlesUpdateCounters();
1133 EXPECT_EQ(1, counters.num_updates_applied);
1134 EXPECT_EQ(1, counters.num_local_overwrites);
1135 EXPECT_EQ(0, counters.num_server_overwrites);
1136 local_metadata = entry_factory()->GetLocalAttachmentMetadataForItem(handle);
1137 EXPECT_EQ(server_metadata.SerializeAsString(),
1138 local_metadata.SerializeAsString());
1139 }
1140
1141 TEST_F(DirectoryUpdateHandlerApplyUpdateTest,
1142 SimpleConflictSameAttachmentMetadataDifferentOrder) {
1143 const bool is_folder = false;
1144 sync_pb::EntitySpecifics specifics;
1145 *specifics.mutable_article() = sync_pb::ArticleSpecifics();
1146 int64_t handle =
1147 entry_factory()->CreateSyncedItem("art1", ARTICLES, is_folder);
1148
1149 sync_pb::AttachmentIdProto id1 = CreateAttachmentIdProto(0, 0);
1150 sync_pb::AttachmentIdProto id2 = CreateAttachmentIdProto(0, 0);
1151
1152 // Add id1, then id2 to the local attachment metadata.
1153 sync_pb::AttachmentMetadata local_metadata;
1154 sync_pb::AttachmentMetadataRecord* record = local_metadata.add_record();
1155 *record->mutable_id() = id1;
1156 record->set_is_on_server(true);
1157 record = local_metadata.add_record();
1158 *record->mutable_id() = id2;
1159 record->set_is_on_server(true);
1160 entry_factory()->SetLocalAttachmentMetadataForItem(handle, local_metadata);
1161
1162 // Add id1 and id2 to the server attachment metadata, but in reverse order.
1163 sync_pb::AttachmentMetadata server_metadata;
1164 record = server_metadata.add_record();
1165 *record->mutable_id() = id2;
1166 record->set_is_on_server(true);
1167 record = local_metadata.add_record();
1168 *record->mutable_id() = id1;
1169 record->set_is_on_server(true);
1170 entry_factory()->SetServerAttachmentMetadataForItem(handle, server_metadata);
1171
1172 // At this point we have a (false) conflict.
1173
1174 sessions::StatusController status;
1175 ApplyArticlesUpdates(&status);
1176
1177 // See that the server won.
1178 const UpdateCounters& counters = GetArticlesUpdateCounters();
1179 EXPECT_EQ(1, counters.num_updates_applied);
1180 EXPECT_EQ(1, counters.num_local_overwrites);
1181 EXPECT_EQ(0, counters.num_server_overwrites);
1182 local_metadata = entry_factory()->GetLocalAttachmentMetadataForItem(handle);
1183 EXPECT_EQ(server_metadata.SerializeAsString(),
1184 local_metadata.SerializeAsString());
1185 }
1186
1187 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/engine/directory_update_handler.cc ('k') | sync/engine/get_commit_ids.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698