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