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

Side by Side Diff: chrome/browser/sync/engine/apply_updates_command_unittest.cc

Issue 9699057: [Sync] Move 'sync' target to sync/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Tim's comments Created 8 years, 9 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <string>
6
7 #include "base/format_macros.h"
8 #include "base/location.h"
9 #include "base/stringprintf.h"
10 #include "chrome/browser/sync/engine/apply_updates_command.h"
11 #include "chrome/browser/sync/engine/nigori_util.h"
12 #include "chrome/browser/sync/engine/syncer.h"
13 #include "chrome/browser/sync/engine/syncer_util.h"
14 #include "chrome/browser/sync/sessions/sync_session.h"
15 #include "chrome/browser/sync/syncable/syncable.h"
16 #include "chrome/browser/sync/syncable/syncable_id.h"
17 #include "chrome/browser/sync/test/engine/fake_model_worker.h"
18 #include "chrome/browser/sync/test/engine/syncer_command_test.h"
19 #include "chrome/browser/sync/test/engine/test_id_factory.h"
20 #include "chrome/browser/sync/test/fake_encryptor.h"
21 #include "chrome/browser/sync/util/cryptographer.h"
22 #include "sync/protocol/bookmark_specifics.pb.h"
23 #include "sync/protocol/password_specifics.pb.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace browser_sync {
27
28 using sessions::SyncSession;
29 using std::string;
30 using syncable::Entry;
31 using syncable::Id;
32 using syncable::MutableEntry;
33 using syncable::ReadTransaction;
34 using syncable::UNITTEST;
35 using syncable::WriteTransaction;
36
37 namespace {
38 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
39 sync_pb::EntitySpecifics result;
40 AddDefaultFieldValue(syncable::BOOKMARKS, &result);
41 return result;
42 }
43 } // namespace
44
45 // A test fixture for tests exercising ApplyUpdatesCommand.
46 class ApplyUpdatesCommandTest : public SyncerCommandTest {
47 public:
48 protected:
49 ApplyUpdatesCommandTest() : next_revision_(1) {}
50 virtual ~ApplyUpdatesCommandTest() {}
51
52 virtual void SetUp() {
53 workers()->clear();
54 mutable_routing_info()->clear();
55 workers()->push_back(
56 make_scoped_refptr(new FakeModelWorker(GROUP_UI)));
57 workers()->push_back(
58 make_scoped_refptr(new FakeModelWorker(GROUP_PASSWORD)));
59 (*mutable_routing_info())[syncable::BOOKMARKS] = GROUP_UI;
60 (*mutable_routing_info())[syncable::PASSWORDS] = GROUP_PASSWORD;
61 (*mutable_routing_info())[syncable::NIGORI] = GROUP_PASSIVE;
62 SyncerCommandTest::SetUp();
63 ExpectNoGroupsToChange(apply_updates_command_);
64 }
65
66 // Create a new unapplied folder node with a parent.
67 void CreateUnappliedNewItemWithParent(
68 const string& item_id,
69 const sync_pb::EntitySpecifics& specifics,
70 const string& parent_id) {
71 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
72 MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
73 Id::CreateFromServerId(item_id));
74 ASSERT_TRUE(entry.good());
75 entry.Put(syncable::SERVER_VERSION, next_revision_++);
76 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
77
78 entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id);
79 entry.Put(syncable::SERVER_PARENT_ID, Id::CreateFromServerId(parent_id));
80 entry.Put(syncable::SERVER_IS_DIR, true);
81 entry.Put(syncable::SERVER_SPECIFICS, specifics);
82 }
83
84 // Create a new unapplied update without a parent.
85 void CreateUnappliedNewItem(const string& item_id,
86 const sync_pb::EntitySpecifics& specifics,
87 bool is_unique) {
88 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
89 MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
90 Id::CreateFromServerId(item_id));
91 ASSERT_TRUE(entry.good());
92 entry.Put(syncable::SERVER_VERSION, next_revision_++);
93 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
94 entry.Put(syncable::SERVER_NON_UNIQUE_NAME, item_id);
95 entry.Put(syncable::SERVER_PARENT_ID, syncable::GetNullId());
96 entry.Put(syncable::SERVER_IS_DIR, false);
97 entry.Put(syncable::SERVER_SPECIFICS, specifics);
98 if (is_unique) // For top-level nodes.
99 entry.Put(syncable::UNIQUE_SERVER_TAG, item_id);
100 }
101
102 // Create an unsynced item in the database. If item_id is a local ID, it
103 // will be treated as a create-new. Otherwise, if it's a server ID, we'll
104 // fake the server data so that it looks like it exists on the server.
105 // Returns the methandle of the created item in |metahandle_out| if not NULL.
106 void CreateUnsyncedItem(const Id& item_id,
107 const Id& parent_id,
108 const string& name,
109 bool is_folder,
110 syncable::ModelType model_type,
111 int64* metahandle_out) {
112 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
113 Id predecessor_id;
114 ASSERT_TRUE(
115 directory()->GetLastChildIdForTest(&trans, parent_id, &predecessor_id));
116 MutableEntry entry(&trans, syncable::CREATE, parent_id, name);
117 ASSERT_TRUE(entry.good());
118 entry.Put(syncable::ID, item_id);
119 entry.Put(syncable::BASE_VERSION,
120 item_id.ServerKnows() ? next_revision_++ : 0);
121 entry.Put(syncable::IS_UNSYNCED, true);
122 entry.Put(syncable::IS_DIR, is_folder);
123 entry.Put(syncable::IS_DEL, false);
124 entry.Put(syncable::PARENT_ID, parent_id);
125 CHECK(entry.PutPredecessor(predecessor_id));
126 sync_pb::EntitySpecifics default_specifics;
127 syncable::AddDefaultFieldValue(model_type, &default_specifics);
128 entry.Put(syncable::SPECIFICS, default_specifics);
129 if (item_id.ServerKnows()) {
130 entry.Put(syncable::SERVER_SPECIFICS, default_specifics);
131 entry.Put(syncable::SERVER_IS_DIR, is_folder);
132 entry.Put(syncable::SERVER_PARENT_ID, parent_id);
133 entry.Put(syncable::SERVER_IS_DEL, false);
134 }
135 if (metahandle_out)
136 *metahandle_out = entry.Get(syncable::META_HANDLE);
137 }
138
139 // Creates an item that is both unsynced an an unapplied update. Returns the
140 // metahandle of the created item.
141 int64 CreateUnappliedAndUnsyncedItem(const string& name,
142 syncable::ModelType model_type) {
143 int64 metahandle = 0;
144 CreateUnsyncedItem(id_factory_.MakeServer(name), id_factory_.root(), name,
145 false, model_type, &metahandle);
146
147 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
148 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, metahandle);
149 if (!entry.good()) {
150 ADD_FAILURE();
151 return syncable::kInvalidMetaHandle;
152 }
153
154 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
155 entry.Put(syncable::SERVER_VERSION, GetNextRevision());
156
157 return metahandle;
158 }
159
160
161 // Creates an item that has neither IS_UNSYNED or IS_UNAPPLIED_UPDATE. The
162 // item is known to both the server and client. Returns the metahandle of
163 // the created item.
164 int64 CreateSyncedItem(const std::string& name, syncable::ModelType
165 model_type, bool is_folder) {
166 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
167
168 syncable::Id parent_id(id_factory_.root());
169 syncable::Id item_id(id_factory_.MakeServer(name));
170 int64 version = GetNextRevision();
171
172 sync_pb::EntitySpecifics default_specifics;
173 syncable::AddDefaultFieldValue(model_type, &default_specifics);
174
175 MutableEntry entry(&trans, syncable::CREATE, parent_id, name);
176 if (!entry.good()) {
177 ADD_FAILURE();
178 return syncable::kInvalidMetaHandle;
179 }
180
181 entry.Put(syncable::ID, item_id);
182 entry.Put(syncable::BASE_VERSION, version);
183 entry.Put(syncable::IS_UNSYNCED, false);
184 entry.Put(syncable::NON_UNIQUE_NAME, name);
185 entry.Put(syncable::IS_DIR, is_folder);
186 entry.Put(syncable::IS_DEL, false);
187 entry.Put(syncable::PARENT_ID, parent_id);
188
189 if (!entry.PutPredecessor(id_factory_.root())) {
190 ADD_FAILURE();
191 return syncable::kInvalidMetaHandle;
192 }
193 entry.Put(syncable::SPECIFICS, default_specifics);
194
195 entry.Put(syncable::SERVER_VERSION, GetNextRevision());
196 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
197 entry.Put(syncable::SERVER_NON_UNIQUE_NAME, "X");
198 entry.Put(syncable::SERVER_PARENT_ID, id_factory_.MakeServer("Y"));
199 entry.Put(syncable::SERVER_IS_DIR, is_folder);
200 entry.Put(syncable::SERVER_IS_DEL, false);
201 entry.Put(syncable::SERVER_SPECIFICS, default_specifics);
202 entry.Put(syncable::SERVER_PARENT_ID, parent_id);
203
204 return entry.Get(syncable::META_HANDLE);
205 }
206
207 int64 GetNextRevision() {
208 return next_revision_++;
209 }
210
211 ApplyUpdatesCommand apply_updates_command_;
212 FakeEncryptor encryptor_;
213 TestIdFactory id_factory_;
214 private:
215 int64 next_revision_;
216 DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest);
217 };
218
219 TEST_F(ApplyUpdatesCommandTest, Simple) {
220 string root_server_id = syncable::GetNullId().GetServerId();
221 CreateUnappliedNewItemWithParent("parent",
222 DefaultBookmarkSpecifics(),
223 root_server_id);
224 CreateUnappliedNewItemWithParent("child",
225 DefaultBookmarkSpecifics(),
226 "parent");
227
228 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
229 apply_updates_command_.ExecuteImpl(session());
230
231 sessions::StatusController* status = session()->mutable_status_controller();
232
233 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
234 ASSERT_TRUE(status->update_progress());
235 EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
236 << "All updates should have been attempted";
237 ASSERT_TRUE(status->conflict_progress());
238 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
239 << "Simple update shouldn't result in conflicts";
240 EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
241 << "Simple update shouldn't result in conflicts";
242 EXPECT_EQ(0, status->conflict_progress()->HierarchyConflictingItemsSize())
243 << "Simple update shouldn't result in conflicts";
244 EXPECT_EQ(2, status->update_progress()->SuccessfullyAppliedUpdateCount())
245 << "All items should have been successfully applied";
246 }
247
248 TEST_F(ApplyUpdatesCommandTest, UpdateWithChildrenBeforeParents) {
249 // Set a bunch of updates which are difficult to apply in the order
250 // they're received due to dependencies on other unseen items.
251 string root_server_id = syncable::GetNullId().GetServerId();
252 CreateUnappliedNewItemWithParent("a_child_created_first",
253 DefaultBookmarkSpecifics(),
254 "parent");
255 CreateUnappliedNewItemWithParent("x_child_created_first",
256 DefaultBookmarkSpecifics(),
257 "parent");
258 CreateUnappliedNewItemWithParent("parent",
259 DefaultBookmarkSpecifics(),
260 root_server_id);
261 CreateUnappliedNewItemWithParent("a_child_created_second",
262 DefaultBookmarkSpecifics(),
263 "parent");
264 CreateUnappliedNewItemWithParent("x_child_created_second",
265 DefaultBookmarkSpecifics(),
266 "parent");
267
268 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
269 apply_updates_command_.ExecuteImpl(session());
270
271 sessions::StatusController* status = session()->mutable_status_controller();
272 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
273 ASSERT_TRUE(status->update_progress());
274 EXPECT_EQ(5, status->update_progress()->AppliedUpdatesSize())
275 << "All updates should have been attempted";
276 ASSERT_TRUE(status->conflict_progress());
277 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
278 << "Simple update shouldn't result in conflicts, even if out-of-order";
279 EXPECT_EQ(5, status->update_progress()->SuccessfullyAppliedUpdateCount())
280 << "All updates should have been successfully applied";
281 }
282
283 // Runs the ApplyUpdatesCommand on an item that has both local and remote
284 // modifications (IS_UNSYNCED and IS_UNAPPLIED_UPDATE). We expect the command
285 // to detect that this update can't be applied because it is in a CONFLICT
286 // state.
287 TEST_F(ApplyUpdatesCommandTest, SimpleConflict) {
288 CreateUnappliedAndUnsyncedItem("item", syncable::BOOKMARKS);
289
290 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
291 apply_updates_command_.ExecuteImpl(session());
292
293 sessions::StatusController* status = session()->mutable_status_controller();
294 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
295 ASSERT_TRUE(status->conflict_progress());
296 EXPECT_EQ(1, status->conflict_progress()->SimpleConflictingItemsSize())
297 << "Unsynced and unapplied item should be a simple conflict";
298 }
299
300 // Runs the ApplyUpdatesCommand on an item that has both local and remote
301 // modifications *and* the remote modification cannot be applied without
302 // violating the tree constraints. We expect the command to detect that this
303 // update can't be applied and that this situation can't be resolved with the
304 // simple conflict processing logic; it is in a CONFLICT_HIERARCHY state.
305 TEST_F(ApplyUpdatesCommandTest, HierarchyAndSimpleConflict) {
306 // Create a simply-conflicting item. It will start with valid parent ids.
307 int64 handle = CreateUnappliedAndUnsyncedItem("orphaned_by_server",
308 syncable::BOOKMARKS);
309 {
310 // Manually set the SERVER_PARENT_ID to bad value.
311 // A bad parent indicates a hierarchy conflict.
312 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
313 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
314 ASSERT_TRUE(entry.good());
315
316 entry.Put(syncable::SERVER_PARENT_ID,
317 id_factory_.MakeServer("bogus_parent"));
318 }
319
320 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
321 apply_updates_command_.ExecuteImpl(session());
322
323 sessions::StatusController* status = session()->mutable_status_controller();
324 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
325
326 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize());
327
328 // An update that is both a simple conflict and a hierarchy conflict should be
329 // treated as a hierarchy conflict.
330 ASSERT_TRUE(status->conflict_progress());
331 EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
332 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize());
333 }
334
335
336 // Runs the ApplyUpdatesCommand on an item with remote modifications that would
337 // create a directory loop if the update were applied. We expect the command to
338 // detect that this update can't be applied because it is in a
339 // CONFLICT_HIERARCHY state.
340 TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDirectoryLoop) {
341 // Item 'X' locally has parent of 'root'. Server is updating it to have
342 // parent of 'Y'.
343 {
344 // Create it as a child of root node.
345 int64 handle = CreateSyncedItem("X", syncable::BOOKMARKS, true);
346
347 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
348 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
349 ASSERT_TRUE(entry.good());
350
351 // Re-parent from root to "Y"
352 entry.Put(syncable::SERVER_VERSION, GetNextRevision());
353 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
354 entry.Put(syncable::SERVER_PARENT_ID, id_factory_.MakeServer("Y"));
355 }
356
357 // Item 'Y' is child of 'X'.
358 CreateUnsyncedItem(id_factory_.MakeServer("Y"), id_factory_.MakeServer("X"),
359 "Y", true, syncable::BOOKMARKS, NULL);
360
361 // If the server's update were applied, we would have X be a child of Y, and Y
362 // as a child of X. That's a directory loop. The UpdateApplicator should
363 // prevent the update from being applied and note that this is a hierarchy
364 // conflict.
365
366 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
367 apply_updates_command_.ExecuteImpl(session());
368
369 sessions::StatusController* status = session()->mutable_status_controller();
370 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
371
372 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize());
373
374 // This should count as a hierarchy conflict.
375 ASSERT_TRUE(status->conflict_progress());
376 EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
377 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize());
378 }
379
380 // Runs the ApplyUpdatesCommand on a directory where the server sent us an
381 // update to add a child to a locally deleted (and unsynced) parent. We expect
382 // the command to not apply the update and to indicate the update is in a
383 // CONFLICT_HIERARCHY state.
384 TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDeletedParent) {
385 // Create a locally deleted parent item.
386 int64 parent_handle;
387 CreateUnsyncedItem(Id::CreateFromServerId("parent"), id_factory_.root(),
388 "parent", true, syncable::BOOKMARKS, &parent_handle);
389 {
390 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
391 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, parent_handle);
392 entry.Put(syncable::IS_DEL, true);
393 }
394
395 // Create an incoming child from the server.
396 CreateUnappliedNewItemWithParent("child", DefaultBookmarkSpecifics(),
397 "parent");
398
399 // The server's update may seem valid to some other client, but on this client
400 // that new item's parent no longer exists. The update should not be applied
401 // and the update applicator should indicate this is a hierarchy conflict.
402
403 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
404 apply_updates_command_.ExecuteImpl(session());
405
406 sessions::StatusController* status = session()->mutable_status_controller();
407 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
408
409 // This should count as a hierarchy conflict.
410 ASSERT_TRUE(status->conflict_progress());
411 EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
412 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize());
413 }
414
415 // Runs the ApplyUpdatesCommand on a directory where the server is trying to
416 // delete a folder that has a recently added (and unsynced) child. We expect
417 // the command to not apply the update because it is in a CONFLICT_HIERARCHY
418 // state.
419 TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDeleteNonEmptyDirectory) {
420 // Create a server-deleted directory.
421 {
422 // Create it as a child of root node.
423 int64 handle = CreateSyncedItem("parent", syncable::BOOKMARKS, true);
424
425 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
426 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
427 ASSERT_TRUE(entry.good());
428
429 // Delete it on the server.
430 entry.Put(syncable::SERVER_VERSION, GetNextRevision());
431 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
432 entry.Put(syncable::SERVER_PARENT_ID, id_factory_.root());
433 entry.Put(syncable::SERVER_IS_DEL, true);
434 }
435
436 // Create a local child of the server-deleted directory.
437 CreateUnsyncedItem(id_factory_.MakeServer("child"),
438 id_factory_.MakeServer("parent"), "child", false,
439 syncable::BOOKMARKS, NULL);
440
441 // The server's request to delete the directory must be ignored, otherwise our
442 // unsynced new child would be orphaned. This is a hierarchy conflict.
443
444 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
445 apply_updates_command_.ExecuteImpl(session());
446
447 sessions::StatusController* status = session()->mutable_status_controller();
448 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
449
450 // This should count as a hierarchy conflict.
451 ASSERT_TRUE(status->conflict_progress());
452 EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
453 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize());
454 }
455
456 // Runs the ApplyUpdatesCommand on a server-created item that has a locally
457 // unknown parent. We expect the command to not apply the update because the
458 // item is in a CONFLICT_HIERARCHY state.
459 TEST_F(ApplyUpdatesCommandTest, HierarchyConflictUnknownParent) {
460 // We shouldn't be able to do anything with either of these items.
461 CreateUnappliedNewItemWithParent("some_item",
462 DefaultBookmarkSpecifics(),
463 "unknown_parent");
464 CreateUnappliedNewItemWithParent("some_other_item",
465 DefaultBookmarkSpecifics(),
466 "some_item");
467
468 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
469 apply_updates_command_.ExecuteImpl(session());
470
471 sessions::StatusController* status = session()->mutable_status_controller();
472 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
473 ASSERT_TRUE(status->update_progress());
474 EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
475 << "All updates should have been attempted";
476 ASSERT_TRUE(status->conflict_progress());
477 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
478 << "Updates with unknown parent should not be treated as 'simple'"
479 << " conflicts";
480 EXPECT_EQ(2, status->conflict_progress()->HierarchyConflictingItemsSize())
481 << "All updates with an unknown ancestors should be in conflict";
482 EXPECT_EQ(0, status->update_progress()->SuccessfullyAppliedUpdateCount())
483 << "No item with an unknown ancestor should be applied";
484 }
485
486 TEST_F(ApplyUpdatesCommandTest, ItemsBothKnownAndUnknown) {
487 // See what happens when there's a mixture of good and bad updates.
488 string root_server_id = syncable::GetNullId().GetServerId();
489 CreateUnappliedNewItemWithParent("first_unknown_item",
490 DefaultBookmarkSpecifics(),
491 "unknown_parent");
492 CreateUnappliedNewItemWithParent("first_known_item",
493 DefaultBookmarkSpecifics(),
494 root_server_id);
495 CreateUnappliedNewItemWithParent("second_unknown_item",
496 DefaultBookmarkSpecifics(),
497 "unknown_parent");
498 CreateUnappliedNewItemWithParent("second_known_item",
499 DefaultBookmarkSpecifics(),
500 "first_known_item");
501 CreateUnappliedNewItemWithParent("third_known_item",
502 DefaultBookmarkSpecifics(),
503 "fourth_known_item");
504 CreateUnappliedNewItemWithParent("fourth_known_item",
505 DefaultBookmarkSpecifics(),
506 root_server_id);
507
508 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
509 apply_updates_command_.ExecuteImpl(session());
510
511 sessions::StatusController* status = session()->mutable_status_controller();
512 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
513 ASSERT_TRUE(status->update_progress());
514 EXPECT_EQ(6, status->update_progress()->AppliedUpdatesSize())
515 << "All updates should have been attempted";
516 ASSERT_TRUE(status->conflict_progress());
517 EXPECT_EQ(2, status->conflict_progress()->HierarchyConflictingItemsSize())
518 << "The updates with unknown ancestors should be in conflict";
519 EXPECT_EQ(4, status->update_progress()->SuccessfullyAppliedUpdateCount())
520 << "The updates with known ancestors should be successfully applied";
521 }
522
523 TEST_F(ApplyUpdatesCommandTest, DecryptablePassword) {
524 // Decryptable password updates should be applied.
525 Cryptographer* cryptographer;
526 {
527 // Storing the cryptographer separately is bad, but for this test we
528 // know it's safe.
529 ReadTransaction trans(FROM_HERE, directory());
530 cryptographer = directory()->GetCryptographer(&trans);
531 }
532
533 browser_sync::KeyParams params = {"localhost", "dummy", "foobar"};
534 cryptographer->AddKey(params);
535
536 sync_pb::EntitySpecifics specifics;
537 sync_pb::PasswordSpecificsData data;
538 data.set_origin("http://example.com");
539
540 cryptographer->Encrypt(data,
541 specifics.mutable_password()->mutable_encrypted());
542 CreateUnappliedNewItem("item", specifics, false);
543
544 ExpectGroupToChange(apply_updates_command_, GROUP_PASSWORD);
545 apply_updates_command_.ExecuteImpl(session());
546
547 sessions::StatusController* status = session()->mutable_status_controller();
548 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSWORD);
549 ASSERT_TRUE(status->update_progress());
550 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
551 << "All updates should have been attempted";
552 ASSERT_TRUE(status->conflict_progress());
553 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
554 << "No update should be in conflict because they're all decryptable";
555 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
556 << "The updates that can be decrypted should be applied";
557 }
558
559 TEST_F(ApplyUpdatesCommandTest, UndecryptableData) {
560 // Undecryptable updates should not be applied.
561 sync_pb::EntitySpecifics encrypted_bookmark;
562 encrypted_bookmark.mutable_encrypted();
563 AddDefaultFieldValue(syncable::BOOKMARKS, &encrypted_bookmark);
564 string root_server_id = syncable::GetNullId().GetServerId();
565 CreateUnappliedNewItemWithParent("folder",
566 encrypted_bookmark,
567 root_server_id);
568 CreateUnappliedNewItem("item2", encrypted_bookmark, false);
569 sync_pb::EntitySpecifics encrypted_password;
570 encrypted_password.mutable_password();
571 CreateUnappliedNewItem("item3", encrypted_password, false);
572
573 ExpectGroupsToChange(apply_updates_command_, GROUP_UI, GROUP_PASSWORD);
574 apply_updates_command_.ExecuteImpl(session());
575
576 sessions::StatusController* status = session()->mutable_status_controller();
577 EXPECT_TRUE(status->HasConflictingUpdates())
578 << "Updates that can't be decrypted should trigger the syncer to have "
579 << "conflicting updates.";
580 {
581 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
582 ASSERT_TRUE(status->update_progress());
583 EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
584 << "All updates should have been attempted";
585 ASSERT_TRUE(status->conflict_progress());
586 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
587 << "The updates that can't be decrypted should not be in regular "
588 << "conflict";
589 EXPECT_EQ(2, status->conflict_progress()->EncryptionConflictingItemsSize())
590 << "The updates that can't be decrypted should be in encryption "
591 << "conflict";
592 EXPECT_EQ(0, status->update_progress()->SuccessfullyAppliedUpdateCount())
593 << "No update that can't be decrypted should be applied";
594 }
595 {
596 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSWORD);
597 ASSERT_TRUE(status->update_progress());
598 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
599 << "All updates should have been attempted";
600 ASSERT_TRUE(status->conflict_progress());
601 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
602 << "The updates that can't be decrypted should not be in regular "
603 << "conflict";
604 EXPECT_EQ(1, status->conflict_progress()->EncryptionConflictingItemsSize())
605 << "The updates that can't be decrypted should be in encryption "
606 << "conflict";
607 EXPECT_EQ(0, status->update_progress()->SuccessfullyAppliedUpdateCount())
608 << "No update that can't be decrypted should be applied";
609 }
610 }
611
612 TEST_F(ApplyUpdatesCommandTest, SomeUndecryptablePassword) {
613 // Only decryptable password updates should be applied.
614 {
615 sync_pb::EntitySpecifics specifics;
616 sync_pb::PasswordSpecificsData data;
617 data.set_origin("http://example.com/1");
618 {
619 ReadTransaction trans(FROM_HERE, directory());
620 Cryptographer* cryptographer = directory()->GetCryptographer(&trans);
621
622 KeyParams params = {"localhost", "dummy", "foobar"};
623 cryptographer->AddKey(params);
624
625 cryptographer->Encrypt(data,
626 specifics.mutable_password()->mutable_encrypted());
627 }
628 CreateUnappliedNewItem("item1", specifics, false);
629 }
630 {
631 // Create a new cryptographer, independent of the one in the session.
632 Cryptographer cryptographer(&encryptor_);
633 KeyParams params = {"localhost", "dummy", "bazqux"};
634 cryptographer.AddKey(params);
635
636 sync_pb::EntitySpecifics specifics;
637 sync_pb::PasswordSpecificsData data;
638 data.set_origin("http://example.com/2");
639
640 cryptographer.Encrypt(data,
641 specifics.mutable_password()->mutable_encrypted());
642 CreateUnappliedNewItem("item2", specifics, false);
643 }
644
645 ExpectGroupToChange(apply_updates_command_, GROUP_PASSWORD);
646 apply_updates_command_.ExecuteImpl(session());
647
648 sessions::StatusController* status = session()->mutable_status_controller();
649 EXPECT_TRUE(status->HasConflictingUpdates())
650 << "Updates that can't be decrypted should trigger the syncer to have "
651 << "conflicting updates.";
652 {
653 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSWORD);
654 ASSERT_TRUE(status->update_progress());
655 EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
656 << "All updates should have been attempted";
657 ASSERT_TRUE(status->conflict_progress());
658 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
659 << "The updates that can't be decrypted should not be in regular "
660 << "conflict";
661 EXPECT_EQ(1, status->conflict_progress()->EncryptionConflictingItemsSize())
662 << "The updates that can't be decrypted should be in encryption "
663 << "conflict";
664 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
665 << "The undecryptable password update shouldn't be applied";
666 }
667 }
668
669 TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
670 // Storing the cryptographer separately is bad, but for this test we
671 // know it's safe.
672 Cryptographer* cryptographer;
673 syncable::ModelTypeSet encrypted_types;
674 encrypted_types.Put(syncable::PASSWORDS);
675 encrypted_types.Put(syncable::NIGORI);
676 {
677 ReadTransaction trans(FROM_HERE, directory());
678 cryptographer = directory()->GetCryptographer(&trans);
679 EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(encrypted_types));
680 }
681
682 // Nigori node updates should update the Cryptographer.
683 Cryptographer other_cryptographer(&encryptor_);
684 KeyParams params = {"localhost", "dummy", "foobar"};
685 other_cryptographer.AddKey(params);
686
687 sync_pb::EntitySpecifics specifics;
688 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
689 other_cryptographer.GetKeys(nigori->mutable_encrypted());
690 nigori->set_encrypt_bookmarks(true);
691 encrypted_types.Put(syncable::BOOKMARKS);
692 CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
693 specifics, true);
694 EXPECT_FALSE(cryptographer->has_pending_keys());
695
696 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
697 apply_updates_command_.ExecuteImpl(session());
698
699 sessions::StatusController* status = session()->mutable_status_controller();
700 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
701 ASSERT_TRUE(status->update_progress());
702 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
703 << "All updates should have been attempted";
704 ASSERT_TRUE(status->conflict_progress());
705 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
706 << "The nigori update shouldn't be in conflict";
707 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
708 << "The nigori update should be applied";
709
710 EXPECT_FALSE(cryptographer->is_ready());
711 EXPECT_TRUE(cryptographer->has_pending_keys());
712 EXPECT_TRUE(
713 cryptographer->GetEncryptedTypes()
714 .Equals(syncable::ModelTypeSet::All()));
715 }
716
717 TEST_F(ApplyUpdatesCommandTest, NigoriUpdateForDisabledTypes) {
718 // Storing the cryptographer separately is bad, but for this test we
719 // know it's safe.
720 Cryptographer* cryptographer;
721 syncable::ModelTypeSet encrypted_types;
722 encrypted_types.Put(syncable::PASSWORDS);
723 encrypted_types.Put(syncable::NIGORI);
724 {
725 ReadTransaction trans(FROM_HERE, directory());
726 cryptographer = directory()->GetCryptographer(&trans);
727 EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(encrypted_types));
728 }
729
730 // Nigori node updates should update the Cryptographer.
731 Cryptographer other_cryptographer(&encryptor_);
732 KeyParams params = {"localhost", "dummy", "foobar"};
733 other_cryptographer.AddKey(params);
734
735 sync_pb::EntitySpecifics specifics;
736 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
737 other_cryptographer.GetKeys(nigori->mutable_encrypted());
738 nigori->set_encrypt_sessions(true);
739 nigori->set_encrypt_themes(true);
740 encrypted_types.Put(syncable::SESSIONS);
741 encrypted_types.Put(syncable::THEMES);
742 CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
743 specifics, true);
744 EXPECT_FALSE(cryptographer->has_pending_keys());
745
746 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
747 apply_updates_command_.ExecuteImpl(session());
748
749 sessions::StatusController* status = session()->mutable_status_controller();
750 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
751 ASSERT_TRUE(status->update_progress());
752 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
753 << "All updates should have been attempted";
754 ASSERT_TRUE(status->conflict_progress());
755 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
756 << "The nigori update shouldn't be in conflict";
757 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
758 << "The nigori update should be applied";
759
760 EXPECT_FALSE(cryptographer->is_ready());
761 EXPECT_TRUE(cryptographer->has_pending_keys());
762 EXPECT_TRUE(
763 cryptographer->GetEncryptedTypes()
764 .Equals(syncable::ModelTypeSet::All()));
765 }
766
767 // Create some local unsynced and unencrypted data. Apply a nigori update that
768 // turns on encryption for the unsynced data. Ensure we properly encrypt the
769 // data as part of the nigori update. Apply another nigori update with no
770 // changes. Ensure we ignore already-encrypted unsynced data and that nothing
771 // breaks.
772 TEST_F(ApplyUpdatesCommandTest, EncryptUnsyncedChanges) {
773 // Storing the cryptographer separately is bad, but for this test we
774 // know it's safe.
775 Cryptographer* cryptographer;
776 syncable::ModelTypeSet encrypted_types;
777 encrypted_types.Put(syncable::PASSWORDS);
778 encrypted_types.Put(syncable::NIGORI);
779 {
780 ReadTransaction trans(FROM_HERE, directory());
781 cryptographer = directory()->GetCryptographer(&trans);
782 EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(encrypted_types));
783
784 // With default encrypted_types, this should be true.
785 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
786
787 Syncer::UnsyncedMetaHandles handles;
788 SyncerUtil::GetUnsyncedEntries(&trans, &handles);
789 EXPECT_TRUE(handles.empty());
790 }
791
792 // Create unsynced bookmarks without encryption.
793 // First item is a folder
794 Id folder_id = id_factory_.NewLocalId();
795 CreateUnsyncedItem(folder_id, id_factory_.root(), "folder",
796 true, syncable::BOOKMARKS, NULL);
797 // Next five items are children of the folder
798 size_t i;
799 size_t batch_s = 5;
800 for (i = 0; i < batch_s; ++i) {
801 CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
802 base::StringPrintf("Item %"PRIuS"", i), false,
803 syncable::BOOKMARKS, NULL);
804 }
805 // Next five items are children of the root.
806 for (; i < 2*batch_s; ++i) {
807 CreateUnsyncedItem(id_factory_.NewLocalId(), id_factory_.root(),
808 base::StringPrintf("Item %"PRIuS"", i), false,
809 syncable::BOOKMARKS, NULL);
810 }
811
812 KeyParams params = {"localhost", "dummy", "foobar"};
813 cryptographer->AddKey(params);
814 sync_pb::EntitySpecifics specifics;
815 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
816 cryptographer->GetKeys(nigori->mutable_encrypted());
817 nigori->set_encrypt_bookmarks(true);
818 encrypted_types.Put(syncable::BOOKMARKS);
819 CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
820 specifics, true);
821 EXPECT_FALSE(cryptographer->has_pending_keys());
822 EXPECT_TRUE(cryptographer->is_ready());
823
824 {
825 // Ensure we have unsynced nodes that aren't properly encrypted.
826 ReadTransaction trans(FROM_HERE, directory());
827 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
828
829 Syncer::UnsyncedMetaHandles handles;
830 SyncerUtil::GetUnsyncedEntries(&trans, &handles);
831 EXPECT_EQ(2*batch_s+1, handles.size());
832 }
833
834 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
835 apply_updates_command_.ExecuteImpl(session());
836
837 {
838 sessions::StatusController* status = session()->mutable_status_controller();
839 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
840 ASSERT_TRUE(status->update_progress());
841 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
842 << "All updates should have been attempted";
843 ASSERT_TRUE(status->conflict_progress());
844 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
845 << "No updates should be in conflict";
846 EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
847 << "No updates should be in conflict";
848 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
849 << "The nigori update should be applied";
850 }
851 EXPECT_FALSE(cryptographer->has_pending_keys());
852 EXPECT_TRUE(cryptographer->is_ready());
853 {
854 ReadTransaction trans(FROM_HERE, directory());
855
856 // If ProcessUnsyncedChangesForEncryption worked, all our unsynced changes
857 // should be encrypted now.
858 EXPECT_TRUE(syncable::ModelTypeSet::All().Equals(
859 cryptographer->GetEncryptedTypes()));
860 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
861
862 Syncer::UnsyncedMetaHandles handles;
863 SyncerUtil::GetUnsyncedEntries(&trans, &handles);
864 EXPECT_EQ(2*batch_s+1, handles.size());
865 }
866
867 // Simulate another nigori update that doesn't change anything.
868 {
869 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
870 MutableEntry entry(&trans, syncable::GET_BY_SERVER_TAG,
871 syncable::ModelTypeToRootTag(syncable::NIGORI));
872 ASSERT_TRUE(entry.good());
873 entry.Put(syncable::SERVER_VERSION, GetNextRevision());
874 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
875 }
876 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
877 apply_updates_command_.ExecuteImpl(session());
878 {
879 sessions::StatusController* status = session()->mutable_status_controller();
880 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
881 ASSERT_TRUE(status->update_progress());
882 EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
883 << "All updates should have been attempted";
884 ASSERT_TRUE(status->conflict_progress());
885 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
886 << "No updates should be in conflict";
887 EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
888 << "No updates should be in conflict";
889 EXPECT_EQ(2, status->update_progress()->SuccessfullyAppliedUpdateCount())
890 << "The nigori update should be applied";
891 }
892 EXPECT_FALSE(cryptographer->has_pending_keys());
893 EXPECT_TRUE(cryptographer->is_ready());
894 {
895 ReadTransaction trans(FROM_HERE, directory());
896
897 // All our changes should still be encrypted.
898 EXPECT_TRUE(syncable::ModelTypeSet::All().Equals(
899 cryptographer->GetEncryptedTypes()));
900 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
901
902 Syncer::UnsyncedMetaHandles handles;
903 SyncerUtil::GetUnsyncedEntries(&trans, &handles);
904 EXPECT_EQ(2*batch_s+1, handles.size());
905 }
906 }
907
908 TEST_F(ApplyUpdatesCommandTest, CannotEncryptUnsyncedChanges) {
909 // Storing the cryptographer separately is bad, but for this test we
910 // know it's safe.
911 Cryptographer* cryptographer;
912 syncable::ModelTypeSet encrypted_types;
913 encrypted_types.Put(syncable::PASSWORDS);
914 encrypted_types.Put(syncable::NIGORI);
915 {
916 ReadTransaction trans(FROM_HERE, directory());
917 cryptographer = directory()->GetCryptographer(&trans);
918 EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(encrypted_types));
919
920 // With default encrypted_types, this should be true.
921 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
922
923 Syncer::UnsyncedMetaHandles handles;
924 SyncerUtil::GetUnsyncedEntries(&trans, &handles);
925 EXPECT_TRUE(handles.empty());
926 }
927
928 // Create unsynced bookmarks without encryption.
929 // First item is a folder
930 Id folder_id = id_factory_.NewLocalId();
931 CreateUnsyncedItem(folder_id, id_factory_.root(), "folder", true,
932 syncable::BOOKMARKS, NULL);
933 // Next five items are children of the folder
934 size_t i;
935 size_t batch_s = 5;
936 for (i = 0; i < batch_s; ++i) {
937 CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
938 base::StringPrintf("Item %"PRIuS"", i), false,
939 syncable::BOOKMARKS, NULL);
940 }
941 // Next five items are children of the root.
942 for (; i < 2*batch_s; ++i) {
943 CreateUnsyncedItem(id_factory_.NewLocalId(), id_factory_.root(),
944 base::StringPrintf("Item %"PRIuS"", i), false,
945 syncable::BOOKMARKS, NULL);
946 }
947
948 // We encrypt with new keys, triggering the local cryptographer to be unready
949 // and unable to decrypt data (once updated).
950 Cryptographer other_cryptographer(&encryptor_);
951 KeyParams params = {"localhost", "dummy", "foobar"};
952 other_cryptographer.AddKey(params);
953 sync_pb::EntitySpecifics specifics;
954 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
955 other_cryptographer.GetKeys(nigori->mutable_encrypted());
956 nigori->set_encrypt_bookmarks(true);
957 encrypted_types.Put(syncable::BOOKMARKS);
958 CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
959 specifics, true);
960 EXPECT_FALSE(cryptographer->has_pending_keys());
961
962 {
963 // Ensure we have unsynced nodes that aren't properly encrypted.
964 ReadTransaction trans(FROM_HERE, directory());
965 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
966 Syncer::UnsyncedMetaHandles handles;
967 SyncerUtil::GetUnsyncedEntries(&trans, &handles);
968 EXPECT_EQ(2*batch_s+1, handles.size());
969 }
970
971 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
972 apply_updates_command_.ExecuteImpl(session());
973
974 sessions::StatusController* status = session()->mutable_status_controller();
975 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
976 ASSERT_TRUE(status->update_progress());
977 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
978 << "All updates should have been attempted";
979 ASSERT_TRUE(status->conflict_progress());
980 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
981 << "The unsynced changes don't trigger a blocking conflict with the "
982 << "nigori update.";
983 EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
984 << "The unsynced changes don't trigger an encryption conflict with the "
985 << "nigori update.";
986 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
987 << "The nigori update should be applied";
988 EXPECT_FALSE(cryptographer->is_ready());
989 EXPECT_TRUE(cryptographer->has_pending_keys());
990 {
991 ReadTransaction trans(FROM_HERE, directory());
992
993 // Since we have pending keys, we would have failed to encrypt, but the
994 // cryptographer should be updated.
995 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
996 EXPECT_TRUE(cryptographer->GetEncryptedTypes().Equals(
997 syncable::ModelTypeSet().All()));
998 EXPECT_FALSE(cryptographer->is_ready());
999 EXPECT_TRUE(cryptographer->has_pending_keys());
1000
1001 Syncer::UnsyncedMetaHandles handles;
1002 SyncerUtil::GetUnsyncedEntries(&trans, &handles);
1003 EXPECT_EQ(2*batch_s+1, handles.size());
1004 }
1005 }
1006
1007 } // namespace browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/sync/engine/apply_updates_command.cc ('k') | chrome/browser/sync/engine/build_commit_command.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698