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

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

Issue 72403003: sync: Per-type update application (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month 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 | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sync/engine/sync_directory_update_handler.h" 5 #include "sync/engine/sync_directory_update_handler.h"
6 6
7 #include "base/compiler_specific.h" 7 #include "base/compiler_specific.h"
8 #include "base/message_loop/message_loop.h" 8 #include "base/message_loop/message_loop.h"
9 #include "base/stl_util.h"
9 #include "sync/engine/syncer_proto_util.h" 10 #include "sync/engine/syncer_proto_util.h"
10 #include "sync/internal_api/public/base/model_type.h" 11 #include "sync/internal_api/public/base/model_type.h"
12 #include "sync/internal_api/public/test/test_entry_factory.h"
11 #include "sync/protocol/sync.pb.h" 13 #include "sync/protocol/sync.pb.h"
12 #include "sync/sessions/status_controller.h" 14 #include "sync/sessions/status_controller.h"
13 #include "sync/syncable/directory.h" 15 #include "sync/syncable/directory.h"
14 #include "sync/syncable/entry.h" 16 #include "sync/syncable/entry.h"
17 #include "sync/syncable/mutable_entry.h"
15 #include "sync/syncable/syncable_model_neutral_write_transaction.h" 18 #include "sync/syncable/syncable_model_neutral_write_transaction.h"
16 #include "sync/syncable/syncable_proto_util.h" 19 #include "sync/syncable/syncable_proto_util.h"
17 #include "sync/syncable/syncable_read_transaction.h" 20 #include "sync/syncable/syncable_read_transaction.h"
21 #include "sync/syncable/syncable_write_transaction.h"
22 #include "sync/test/engine/fake_model_worker.h"
18 #include "sync/test/engine/test_directory_setter_upper.h" 23 #include "sync/test/engine/test_directory_setter_upper.h"
24 #include "sync/test/engine/test_id_factory.h"
19 #include "sync/test/engine/test_syncable_utils.h" 25 #include "sync/test/engine/test_syncable_utils.h"
20 #include "testing/gtest/include/gtest/gtest.h" 26 #include "testing/gtest/include/gtest/gtest.h"
21 27
22 namespace syncer { 28 namespace syncer {
23 29
24 using syncable::UNITTEST; 30 using syncable::UNITTEST;
25 31
26 class SyncDirectoryUpdateHandlerTest : public ::testing::Test { 32 class SyncDirectoryUpdateHandlerProcessUpdateTest : public ::testing::Test {
Nicolas Zea 2013/11/19 22:45:26 Comment about what parts of the update handler thi
rlarocque 2013/11/21 18:28:47 Done.
27 public: 33 public:
34 SyncDirectoryUpdateHandlerProcessUpdateTest()
35 : ui_worker_(new FakeModelWorker(GROUP_UI)) {
36 }
37
38 virtual ~SyncDirectoryUpdateHandlerProcessUpdateTest() {}
39
28 virtual void SetUp() OVERRIDE { 40 virtual void SetUp() OVERRIDE {
29 dir_maker_.SetUp(); 41 dir_maker_.SetUp();
30 } 42 }
31 43
32 virtual void TearDown() OVERRIDE { 44 virtual void TearDown() OVERRIDE {
33 dir_maker_.TearDown(); 45 dir_maker_.TearDown();
34 } 46 }
35 47
36 syncable::Directory* dir() { 48 syncable::Directory* dir() {
37 return dir_maker_.directory(); 49 return dir_maker_.directory();
38 } 50 }
39 protected: 51 protected:
40 scoped_ptr<sync_pb::SyncEntity> CreateUpdate( 52 scoped_ptr<sync_pb::SyncEntity> CreateUpdate(
41 const std::string& id, 53 const std::string& id,
42 const std::string& parent, 54 const std::string& parent,
43 const ModelType& type); 55 const ModelType& type);
44 56
45 // This exists mostly to give tests access to the protected member function. 57 // This exists mostly to give tests access to the protected member function.
46 // Warning: This takes the syncable directory lock. 58 // Warning: This takes the syncable directory lock.
47 void UpdateSyncEntities( 59 void UpdateSyncEntities(
48 SyncDirectoryUpdateHandler* handler, 60 SyncDirectoryUpdateHandler* handler,
49 const SyncEntityList& applicable_updates, 61 const SyncEntityList& applicable_updates,
50 sessions::StatusController* status); 62 sessions::StatusController* status);
51 63
52 // Another function to access private member functions. 64 // Another function to access private member functions.
53 void UpdateProgressMarkers( 65 void UpdateProgressMarkers(
54 SyncDirectoryUpdateHandler* handler, 66 SyncDirectoryUpdateHandler* handler,
55 const sync_pb::DataTypeProgressMarker& progress); 67 const sync_pb::DataTypeProgressMarker& progress);
56 68
69 scoped_refptr<FakeModelWorker> ui_worker() {
70 return ui_worker_;
71 }
72
57 private: 73 private:
58 base::MessageLoop loop_; // Needed to initialize the directory. 74 base::MessageLoop loop_; // Needed to initialize the directory.
59 TestDirectorySetterUpper dir_maker_; 75 TestDirectorySetterUpper dir_maker_;
76 scoped_refptr<FakeModelWorker> ui_worker_;
60 }; 77 };
61 78
62 scoped_ptr<sync_pb::SyncEntity> SyncDirectoryUpdateHandlerTest::CreateUpdate( 79 scoped_ptr<sync_pb::SyncEntity>
80 SyncDirectoryUpdateHandlerProcessUpdateTest::CreateUpdate(
63 const std::string& id, 81 const std::string& id,
64 const std::string& parent, 82 const std::string& parent,
65 const ModelType& type) { 83 const ModelType& type) {
66 scoped_ptr<sync_pb::SyncEntity> e(new sync_pb::SyncEntity()); 84 scoped_ptr<sync_pb::SyncEntity> e(new sync_pb::SyncEntity());
67 e->set_id_string(id); 85 e->set_id_string(id);
68 e->set_parent_id_string(parent); 86 e->set_parent_id_string(parent);
69 e->set_non_unique_name(id); 87 e->set_non_unique_name(id);
70 e->set_name(id); 88 e->set_name(id);
71 e->set_version(1000); 89 e->set_version(1000);
72 AddDefaultFieldValue(type, e->mutable_specifics()); 90 AddDefaultFieldValue(type, e->mutable_specifics());
73 return e.Pass(); 91 return e.Pass();
74 } 92 }
75 93
76 void SyncDirectoryUpdateHandlerTest::UpdateSyncEntities( 94 void SyncDirectoryUpdateHandlerProcessUpdateTest::UpdateSyncEntities(
77 SyncDirectoryUpdateHandler* handler, 95 SyncDirectoryUpdateHandler* handler,
78 const SyncEntityList& applicable_updates, 96 const SyncEntityList& applicable_updates,
79 sessions::StatusController* status) { 97 sessions::StatusController* status) {
80 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, UNITTEST, dir()); 98 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, UNITTEST, dir());
81 handler->UpdateSyncEntities(&trans, applicable_updates, status); 99 handler->UpdateSyncEntities(&trans, applicable_updates, status);
82 } 100 }
83 101
84 void SyncDirectoryUpdateHandlerTest::UpdateProgressMarkers( 102 void SyncDirectoryUpdateHandlerProcessUpdateTest::UpdateProgressMarkers(
85 SyncDirectoryUpdateHandler* handler, 103 SyncDirectoryUpdateHandler* handler,
86 const sync_pb::DataTypeProgressMarker& progress) { 104 const sync_pb::DataTypeProgressMarker& progress) {
87 handler->UpdateProgressMarker(progress); 105 handler->UpdateProgressMarker(progress);
88 } 106 }
89 107
90 static const char kCacheGuid[] = "IrcjZ2jyzHDV9Io4+zKcXQ=="; 108 static const char kCacheGuid[] = "IrcjZ2jyzHDV9Io4+zKcXQ==";
91 109
92 // Test that the bookmark tag is set on newly downloaded items. 110 // Test that the bookmark tag is set on newly downloaded items.
93 TEST_F(SyncDirectoryUpdateHandlerTest, NewBookmarkTag) { 111 TEST_F(SyncDirectoryUpdateHandlerProcessUpdateTest, NewBookmarkTag) {
94 SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS); 112 SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
95 sync_pb::GetUpdatesResponse gu_response; 113 sync_pb::GetUpdatesResponse gu_response;
96 sessions::StatusController status; 114 sessions::StatusController status;
97 115
98 // Add a bookmark item to the update message. 116 // Add a bookmark item to the update message.
99 std::string root = syncable::GetNullId().GetServerId(); 117 std::string root = syncable::GetNullId().GetServerId();
100 syncable::Id server_id = syncable::Id::CreateFromServerId("b1"); 118 syncable::Id server_id = syncable::Id::CreateFromServerId("b1");
101 scoped_ptr<sync_pb::SyncEntity> e = 119 scoped_ptr<sync_pb::SyncEntity> e =
102 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS); 120 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
103 e->set_originator_cache_guid( 121 e->set_originator_cache_guid(
104 std::string(kCacheGuid, arraysize(kCacheGuid)-1)); 122 std::string(kCacheGuid, arraysize(kCacheGuid)-1));
(...skipping 15 matching lines...) Expand all
120 EXPECT_TRUE(entry.GetServerUniquePosition().IsValid()); 138 EXPECT_TRUE(entry.GetServerUniquePosition().IsValid());
121 139
122 // If this assertion fails, that might indicate that the algorithm used to 140 // If this assertion fails, that might indicate that the algorithm used to
123 // generate bookmark tags has been modified. This could have implications for 141 // generate bookmark tags has been modified. This could have implications for
124 // bookmark ordering. Please make sure you know what you're doing if you 142 // bookmark ordering. Please make sure you know what you're doing if you
125 // intend to make such a change. 143 // intend to make such a change.
126 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry.GetUniqueBookmarkTag()); 144 EXPECT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", entry.GetUniqueBookmarkTag());
127 } 145 }
128 146
129 // Test the receipt of a type root node. 147 // Test the receipt of a type root node.
130 TEST_F(SyncDirectoryUpdateHandlerTest, ReceiveServerCreatedBookmarkFolders) { 148 TEST_F(SyncDirectoryUpdateHandlerProcessUpdateTest,
131 SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS); 149 ReceiveServerCreatedBookmarkFolders) {
150 SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
132 sync_pb::GetUpdatesResponse gu_response; 151 sync_pb::GetUpdatesResponse gu_response;
133 sessions::StatusController status; 152 sessions::StatusController status;
134 153
135 // Create an update that mimics the bookmark root. 154 // Create an update that mimics the bookmark root.
136 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz"); 155 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
137 std::string root = syncable::GetNullId().GetServerId(); 156 std::string root = syncable::GetNullId().GetServerId();
138 scoped_ptr<sync_pb::SyncEntity> e = 157 scoped_ptr<sync_pb::SyncEntity> e =
139 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS); 158 CreateUpdate(SyncableIdToProto(server_id), root, BOOKMARKS);
140 e->set_server_defined_unique_tag("google_chrome_bookmarks"); 159 e->set_server_defined_unique_tag("google_chrome_bookmarks");
141 e->set_folder(true); 160 e->set_folder(true);
(...skipping 12 matching lines...) Expand all
154 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id); 173 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
155 ASSERT_TRUE(entry.good()); 174 ASSERT_TRUE(entry.good());
156 175
157 EXPECT_FALSE(entry.ShouldMaintainPosition()); 176 EXPECT_FALSE(entry.ShouldMaintainPosition());
158 EXPECT_FALSE(entry.GetUniquePosition().IsValid()); 177 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
159 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid()); 178 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
160 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty()); 179 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
161 } 180 }
162 181
163 // Test the receipt of a non-bookmark item. 182 // Test the receipt of a non-bookmark item.
164 TEST_F(SyncDirectoryUpdateHandlerTest, ReceiveNonBookmarkItem) { 183 TEST_F(SyncDirectoryUpdateHandlerProcessUpdateTest, ReceiveNonBookmarkItem) {
165 SyncDirectoryUpdateHandler handler(dir(), AUTOFILL); 184 SyncDirectoryUpdateHandler handler(dir(), PREFERENCES, ui_worker());
166 sync_pb::GetUpdatesResponse gu_response; 185 sync_pb::GetUpdatesResponse gu_response;
167 sessions::StatusController status; 186 sessions::StatusController status;
168 187
169 std::string root = syncable::GetNullId().GetServerId(); 188 std::string root = syncable::GetNullId().GetServerId();
170 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz"); 189 syncable::Id server_id = syncable::Id::CreateFromServerId("xyz");
171 scoped_ptr<sync_pb::SyncEntity> e = 190 scoped_ptr<sync_pb::SyncEntity> e =
172 CreateUpdate(SyncableIdToProto(server_id), root, AUTOFILL); 191 CreateUpdate(SyncableIdToProto(server_id), root, PREFERENCES);
173 e->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I="); 192 e->set_server_defined_unique_tag("9PGRuKdX5sHyGMB17CvYTXuC43I=");
174 193
175 // Add it to the applicable updates list. 194 // Add it to the applicable updates list.
176 SyncEntityList autofill_updates; 195 SyncEntityList autofill_updates;
177 autofill_updates.push_back(e.get()); 196 autofill_updates.push_back(e.get());
178 197
179 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e)); 198 EXPECT_FALSE(SyncerProtoUtil::ShouldMaintainPosition(*e));
180 199
181 // Process it. 200 // Process it.
182 UpdateSyncEntities(&handler, autofill_updates, &status); 201 UpdateSyncEntities(&handler, autofill_updates, &status);
183 202
184 syncable::ReadTransaction trans(FROM_HERE, dir()); 203 syncable::ReadTransaction trans(FROM_HERE, dir());
185 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id); 204 syncable::Entry entry(&trans, syncable::GET_BY_ID, server_id);
186 ASSERT_TRUE(entry.good()); 205 ASSERT_TRUE(entry.good());
187 206
188 EXPECT_FALSE(entry.ShouldMaintainPosition()); 207 EXPECT_FALSE(entry.ShouldMaintainPosition());
189 EXPECT_FALSE(entry.GetUniquePosition().IsValid()); 208 EXPECT_FALSE(entry.GetUniquePosition().IsValid());
190 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid()); 209 EXPECT_FALSE(entry.GetServerUniquePosition().IsValid());
191 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty()); 210 EXPECT_TRUE(entry.GetUniqueBookmarkTag().empty());
192 } 211 }
193 212
194 // Tests the setting of progress markers. 213 // Tests the setting of progress markers.
195 TEST_F(SyncDirectoryUpdateHandlerTest, ProcessNewProgressMarkers) { 214 TEST_F(SyncDirectoryUpdateHandlerProcessUpdateTest, ProcessNewProgressMarkers) {
196 SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS); 215 SyncDirectoryUpdateHandler handler(dir(), BOOKMARKS, ui_worker());
197 216
198 sync_pb::DataTypeProgressMarker progress; 217 sync_pb::DataTypeProgressMarker progress;
199 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS)); 218 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(BOOKMARKS));
200 progress.set_token("token"); 219 progress.set_token("token");
201 220
202 UpdateProgressMarkers(&handler, progress); 221 UpdateProgressMarkers(&handler, progress);
203 222
204 sync_pb::DataTypeProgressMarker saved; 223 sync_pb::DataTypeProgressMarker saved;
205 dir()->GetDownloadProgress(BOOKMARKS, &saved); 224 dir()->GetDownloadProgress(BOOKMARKS, &saved);
206 225
207 EXPECT_EQ(progress.token(), saved.token()); 226 EXPECT_EQ(progress.token(), saved.token());
208 EXPECT_EQ(progress.data_type_id(), saved.data_type_id()); 227 EXPECT_EQ(progress.data_type_id(), saved.data_type_id());
209 } 228 }
210 229
230 class SyncDirectoryUpdateHandlerApplyUpdateTest : public ::testing::Test {
Nicolas Zea 2013/11/19 22:45:26 Here too? (and why it's separate/different)
rlarocque 2013/11/21 18:28:47 Done. The reasons are: - History. - Update applic
231 public:
232 SyncDirectoryUpdateHandlerApplyUpdateTest()
233 : ui_worker_(new FakeModelWorker(GROUP_UI)),
234 password_worker_(new FakeModelWorker(GROUP_PASSWORD)),
235 passive_worker_(new FakeModelWorker(GROUP_PASSIVE)),
236 update_handler_map_deleter_(&update_handler_map_) {}
237
238 virtual void SetUp() OVERRIDE {
239 dir_maker_.SetUp();
240 entry_factory_.reset(new TestEntryFactory(directory()));
241
242 update_handler_map_.insert(std::make_pair(
243 BOOKMARKS,
244 new SyncDirectoryUpdateHandler(directory(), BOOKMARKS, ui_worker_)));
245 update_handler_map_.insert(std::make_pair(
246 PASSWORDS,
247 new SyncDirectoryUpdateHandler(directory(),
248 PASSWORDS,
249 password_worker_)));
250 }
251
252 virtual void TearDown() OVERRIDE {
253 dir_maker_.TearDown();
254 }
255
256 protected:
257 void ApplyBookmarkUpdates(sessions::StatusController* status) {
258 update_handler_map_[BOOKMARKS]->ApplyUpdates(status);
259 }
260
261 void ApplyPasswordUpdates(sessions::StatusController* status) {
262 update_handler_map_[PASSWORDS]->ApplyUpdates(status);
263 }
264
265 TestEntryFactory* entry_factory() {
266 return entry_factory_.get();
267 }
268
269 syncable::Directory* directory() {
270 return dir_maker_.directory();
271 }
272
273 private:
274 base::MessageLoop loop_; // Needed to initialize the directory.
275 TestDirectorySetterUpper dir_maker_;
276 scoped_ptr<TestEntryFactory> entry_factory_;
277
278 scoped_refptr<FakeModelWorker> ui_worker_;
279 scoped_refptr<FakeModelWorker> password_worker_;
280 scoped_refptr<FakeModelWorker> passive_worker_;
281
282 UpdateHandlerMap update_handler_map_;
283 STLValueDeleter<UpdateHandlerMap> update_handler_map_deleter_;
284 };
285
286 namespace {
287 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
288 sync_pb::EntitySpecifics result;
289 AddDefaultFieldValue(BOOKMARKS, &result);
290 return result;
291 }
292 } // namespace
293
294 // Test update application for a few bookmark items.
295 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, SimpleBookmark) {
296 sessions::StatusController status;
297
298 std::string root_server_id = syncable::GetNullId().GetServerId();
299 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
300 "parent", DefaultBookmarkSpecifics(), root_server_id);
301 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
302 "child", DefaultBookmarkSpecifics(), "parent");
303
304 ApplyBookmarkUpdates(&status);
305
306 EXPECT_EQ(0, status.num_encryption_conflicts())
Nicolas Zea 2013/11/19 22:45:26 Check the is_unsynced/is_unapplied bits are set ap
rlarocque 2013/11/21 18:28:47 Good idea. We won't be able to rely on StatusCont
307 << "Simple update shouldn't result in conflicts";
308 EXPECT_EQ(0, status.num_hierarchy_conflicts())
309 << "Simple update shouldn't result in conflicts";
310 EXPECT_EQ(2, status.num_updates_applied())
311 << "All items should have been successfully applied";
312 }
313
314 // Test that the applicator can handle updates delivered out of order.
315 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
316 BookmarkChildrenBeforeParent) {
317 // Start with some bookmarks whose parents are unknown.
318 std::string root_server_id = syncable::GetNullId().GetServerId();
319 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
320 "a_child_created_first", DefaultBookmarkSpecifics(), "parent");
321 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
322 "x_child_created_first", DefaultBookmarkSpecifics(), "parent");
323
324 // Update application will fail.
325 sessions::StatusController status1;
326 ApplyBookmarkUpdates(&status1);
327 EXPECT_EQ(0, status1.num_updates_applied());
328 EXPECT_EQ(2, status1.num_hierarchy_conflicts());
329
330 // Now add their parent and a few siblings.
331 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
332 "parent", DefaultBookmarkSpecifics(), root_server_id);
333 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
334 "a_child_created_second", DefaultBookmarkSpecifics(), "parent");
335 entry_factory()->CreateUnappliedNewBookmarkItemWithParent(
336 "x_child_created_second", DefaultBookmarkSpecifics(), "parent");
337
338 // Update application will succeed.
339 sessions::StatusController status2;
340 ApplyBookmarkUpdates(&status2);
341 EXPECT_EQ(5, status2.num_updates_applied())
342 << "All updates should have been successfully applied";
343 }
344
345 // Try to apply changes on an item that is both IS_UNSYNCED and
346 // IS_UNAPPLIED_UPDATE. Conflict resolution should be performed.
347 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, SimpleBookmarkConflict) {
348 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem("x");
349
350 int original_server_version = -10;
351 {
352 syncable::ReadTransaction trans(FROM_HERE, directory());
353 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
354 original_server_version = e.GetServerVersion();
355 ASSERT_NE(original_server_version, e.GetBaseVersion());
356 }
357
358 sessions::StatusController status;
359 ApplyBookmarkUpdates(&status);
360 EXPECT_EQ(1, status.num_server_overwrites())
361 << "Unsynced and unapplied item conflict should be resolved";
362 EXPECT_EQ(0, status.num_updates_applied())
363 << "Update should not be applied; we should override the server.";
364
365 {
366 syncable::ReadTransaction trans(FROM_HERE, directory());
367 syncable::Entry e(&trans, syncable::GET_BY_HANDLE, handle);
368 EXPECT_EQ(original_server_version, e.GetServerVersion());
369 EXPECT_EQ(original_server_version, e.GetBaseVersion());
370 }
371 }
372
373 // Create a simple conflict that is also a hierarchy conflict. If we were to
374 // follow the normal "server wins" logic, we'd end up violating hierarchy
375 // constraints. The hierarchy conflict must take precedence. We can not allow
376 // the update to be applied. The item must remain in the conflict state.
377 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, HierarchyAndSimpleConflict) {
378 // Create a simply-conflicting item. It will start with valid parent ids.
379 int64 handle = entry_factory()->CreateUnappliedAndUnsyncedBookmarkItem(
380 "orphaned_by_server");
381 {
382 // Manually set the SERVER_PARENT_ID to bad value.
383 // A bad parent indicates a hierarchy conflict.
384 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
385 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
386 ASSERT_TRUE(entry.good());
387
388 entry.PutServerParentId(TestIdFactory::MakeServer("bogus_parent"));
389 }
390
391 sessions::StatusController status;
392 ApplyBookmarkUpdates(&status);
393 EXPECT_EQ(0, status.num_updates_applied());
394 EXPECT_EQ(0, status.num_server_overwrites());
395 EXPECT_EQ(1, status.num_hierarchy_conflicts());
396 }
397
398 // Attempt to apply an udpate that would create a bookmark folder loop. This
399 // application should fail.
400 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, BookmarkFolderLoop) {
401 // Item 'X' locally has parent of 'root'. Server is updating it to have
402 // parent of 'Y'.
403 {
404 // Create it as a child of root node.
405 int64 handle = entry_factory()->CreateSyncedItem("X", BOOKMARKS, true);
406
407 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
408 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
409 ASSERT_TRUE(entry.good());
410
411 // Re-parent from root to "Y"
412 entry.PutServerVersion(entry_factory()->GetNextRevision());
413 entry.PutIsUnappliedUpdate(true);
414 entry.PutServerParentId(TestIdFactory::MakeServer("Y"));
415 }
416
417 // Item 'Y' is child of 'X'.
418 entry_factory()->CreateUnsyncedItem(
419 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
420 BOOKMARKS, NULL);
421
422 // If the server's update were applied, we would have X be a child of Y, and Y
423 // as a child of X. That's a directory loop. The UpdateApplicator should
424 // prevent the update from being applied and note that this is a hierarchy
425 // conflict.
426
427 sessions::StatusController status;
428 ApplyBookmarkUpdates(&status);
429
430 // This should count as a hierarchy conflict.
431 EXPECT_EQ(1, status.num_hierarchy_conflicts());
432 }
433
434 // Test update application where the update has been orphaned by a local folder
435 // deletion. The update application attempt should fail.
436 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
437 HierarchyConflictDeletedParent) {
438 // Create a locally deleted parent item.
439 int64 parent_handle;
440 entry_factory()->CreateUnsyncedItem(
441 syncable::Id::CreateFromServerId("parent"), TestIdFactory::root(),
442 "parent", true, BOOKMARKS, &parent_handle);
443 {
444 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
445 syncable::MutableEntry entry(&trans,
446 syncable::GET_BY_HANDLE,
447 parent_handle);
448 entry.PutIsDel(true);
449 }
450
451 // Create an incoming child from the server.
452 entry_factory()->CreateUnappliedNewItemWithParent(
453 "child", DefaultBookmarkSpecifics(), "parent");
454
455 // The server's update may seem valid to some other client, but on this client
456 // that new item's parent no longer exists. The update should not be applied
457 // and the update applicator should indicate this is a hierarchy conflict.
458
459 sessions::StatusController status;
460 ApplyBookmarkUpdates(&status);
461 EXPECT_EQ(1, status.num_hierarchy_conflicts());
462 }
463
464 // Attempt to apply an update that deletes a folder where the folder has
465 // locally-created children. The update application should fail.
466 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
467 HierarchyConflictDeleteNonEmptyDirectory) {
468 // Create a server-deleted directory.
469 {
470 // Create it as a child of root node.
471 int64 handle = entry_factory()->CreateSyncedItem("parent", BOOKMARKS, true);
472
473 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
474 syncable::MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
475 ASSERT_TRUE(entry.good());
476
477 // Delete it on the server.
478 entry.PutServerVersion(entry_factory()->GetNextRevision());
479 entry.PutIsUnappliedUpdate(true);
480 entry.PutServerParentId(TestIdFactory::root());
481 entry.PutServerIsDel(true);
482 }
483
484 // Create a local child of the server-deleted directory.
485 entry_factory()->CreateUnsyncedItem(
486 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
487 "child", false, BOOKMARKS, NULL);
488
489 // The server's request to delete the directory must be ignored, otherwise our
490 // unsynced new child would be orphaned. This is a hierarchy conflict.
491
492 sessions::StatusController status;
493 ApplyBookmarkUpdates(&status);
494
495 // This should count as a hierarchy conflict.
496 EXPECT_EQ(1, status.num_hierarchy_conflicts());
497 }
498
499 // Attempt to apply updates where the updated item's parent is not known to this
500 // client. The update application attempt should fail.
501 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest,
502 HierarchyConflictUnknownParent) {
503 // We shouldn't be able to do anything with either of these items.
504 entry_factory()->CreateUnappliedNewItemWithParent(
505 "some_item", DefaultBookmarkSpecifics(), "unknown_parent");
506 entry_factory()->CreateUnappliedNewItemWithParent(
507 "some_other_item", DefaultBookmarkSpecifics(), "some_item");
508
509 sessions::StatusController status;
510 ApplyBookmarkUpdates(&status);
511
512 EXPECT_EQ(2, status.num_hierarchy_conflicts())
513 << "All updates with an unknown ancestors should be in conflict";
514 EXPECT_EQ(0, status.num_updates_applied())
515 << "No item with an unknown ancestor should be applied";
516 }
517
518 // Attempt application of a mix of items. Some update application attempts will
519 // fail due to hierarchy conflicts. Others should succeed.
520 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, ItemsBothKnownAndUnknown) {
521 // See what happens when there's a mixture of good and bad updates.
522 std::string root_server_id = syncable::GetNullId().GetServerId();
523 entry_factory()->CreateUnappliedNewItemWithParent(
524 "first_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
525 entry_factory()->CreateUnappliedNewItemWithParent(
526 "first_known_item", DefaultBookmarkSpecifics(), root_server_id);
527 entry_factory()->CreateUnappliedNewItemWithParent(
528 "second_unknown_item", DefaultBookmarkSpecifics(), "unknown_parent");
529 entry_factory()->CreateUnappliedNewItemWithParent(
530 "second_known_item", DefaultBookmarkSpecifics(), "first_known_item");
531 entry_factory()->CreateUnappliedNewItemWithParent(
532 "third_known_item", DefaultBookmarkSpecifics(), "fourth_known_item");
533 entry_factory()->CreateUnappliedNewItemWithParent(
534 "fourth_known_item", DefaultBookmarkSpecifics(), root_server_id);
535
536 sessions::StatusController status;
537 ApplyBookmarkUpdates(&status);
538
539 EXPECT_EQ(2, status.num_hierarchy_conflicts())
540 << "The updates with unknown ancestors should be in conflict";
541 EXPECT_EQ(4, status.num_updates_applied())
542 << "The updates with known ancestors should be successfully applied";
543 }
544
545 // Attempt application of password upates where the passphrase is known.
546 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, DecryptablePassword) {
547 // Decryptable password updates should be applied.
548 Cryptographer* cryptographer;
549 {
550 // Storing the cryptographer separately is bad, but for this test we
551 // know it's safe.
552 syncable::ReadTransaction trans(FROM_HERE, directory());
553 cryptographer = directory()->GetCryptographer(&trans);
554 }
555
556 KeyParams params = {"localhost", "dummy", "foobar"};
557 cryptographer->AddKey(params);
558
559 sync_pb::EntitySpecifics specifics;
560 sync_pb::PasswordSpecificsData data;
561 data.set_origin("http://example.com");
562
563 cryptographer->Encrypt(data,
564 specifics.mutable_password()->mutable_encrypted());
565 entry_factory()->CreateUnappliedNewItem("item", specifics, false);
566
567 sessions::StatusController status;
568 ApplyPasswordUpdates(&status);
569
570 EXPECT_EQ(1, status.num_updates_applied())
571 << "The updates that can be decrypted should be applied";
572 }
573
574 // Attempt application of encrypted items when the passphrase is not known.
575 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, UndecryptableData) {
576 // Undecryptable updates should not be applied.
577 sync_pb::EntitySpecifics encrypted_bookmark;
578 encrypted_bookmark.mutable_encrypted();
579 AddDefaultFieldValue(BOOKMARKS, &encrypted_bookmark);
580 std::string root_server_id = syncable::GetNullId().GetServerId();
581 entry_factory()->CreateUnappliedNewItemWithParent(
582 "folder", encrypted_bookmark, root_server_id);
583 entry_factory()->CreateUnappliedNewItem("item2", encrypted_bookmark, false);
584 sync_pb::EntitySpecifics encrypted_password;
585 encrypted_password.mutable_password();
586 entry_factory()->CreateUnappliedNewItem("item3", encrypted_password, false);
587
588 sessions::StatusController status;
589 ApplyBookmarkUpdates(&status);
590 ApplyPasswordUpdates(&status);
591
592 EXPECT_EQ(3, status.num_encryption_conflicts())
593 << "Updates that can't be decrypted should be in encryption conflict";
594 EXPECT_EQ(0, status.num_updates_applied())
595 << "No update that can't be decrypted should be applied";
596 }
597
598 // Test a mix of decryptable and undecryptable updates.
599 TEST_F(SyncDirectoryUpdateHandlerApplyUpdateTest, SomeUndecryptablePassword) {
600 Cryptographer* cryptographer;
601 // Only decryptable password updates should be applied.
602 {
603 sync_pb::EntitySpecifics specifics;
604 sync_pb::PasswordSpecificsData data;
605 data.set_origin("http://example.com/1");
606 {
607 syncable::ReadTransaction trans(FROM_HERE, directory());
608 cryptographer = directory()->GetCryptographer(&trans);
609
610 KeyParams params = {"localhost", "dummy", "foobar"};
611 cryptographer->AddKey(params);
612
613 cryptographer->Encrypt(data,
614 specifics.mutable_password()->mutable_encrypted());
615 }
616 entry_factory()->CreateUnappliedNewItem("item1", specifics, false);
617 }
618 {
619 // Create a new cryptographer, independent of the one in the session.
620 Cryptographer other_cryptographer(cryptographer->encryptor());
621 KeyParams params = {"localhost", "dummy", "bazqux"};
622 other_cryptographer.AddKey(params);
623
624 sync_pb::EntitySpecifics specifics;
625 sync_pb::PasswordSpecificsData data;
626 data.set_origin("http://example.com/2");
627
628 other_cryptographer.Encrypt(data,
629 specifics.mutable_password()->mutable_encrypted());
630 entry_factory()->CreateUnappliedNewItem("item2", specifics, false);
631 }
632
633 sessions::StatusController status;
634 ApplyPasswordUpdates(&status);
635
636 EXPECT_EQ(1, status.num_encryption_conflicts())
637 << "The updates that can't be decrypted should be in encryption "
638 << "conflict";
639 EXPECT_EQ(1, status.num_updates_applied())
640 << "The undecryptable password update shouldn't be applied";
641 }
642
211 } // namespace syncer 643 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698