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

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

Issue 10832286: sync: Introduce control data types (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add ToFullModelTypeSet() function Created 8 years, 3 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 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 <string> 5 #include <string>
6 6
7 #include "base/format_macros.h"
8 #include "base/location.h" 7 #include "base/location.h"
9 #include "base/memory/scoped_ptr.h" 8 #include "base/memory/scoped_ptr.h"
10 #include "base/stringprintf.h" 9 #include "base/stringprintf.h"
11 #include "sync/engine/apply_updates_command.h" 10 #include "sync/engine/apply_updates_command.h"
12 #include "sync/engine/syncer.h" 11 #include "sync/engine/syncer.h"
13 #include "sync/internal_api/public/test/test_entry_factory.h" 12 #include "sync/internal_api/public/test/test_entry_factory.h"
14 #include "sync/protocol/bookmark_specifics.pb.h" 13 #include "sync/protocol/bookmark_specifics.pb.h"
15 #include "sync/protocol/password_specifics.pb.h" 14 #include "sync/protocol/password_specifics.pb.h"
16 #include "sync/sessions/sync_session.h"
17 #include "sync/syncable/mutable_entry.h" 15 #include "sync/syncable/mutable_entry.h"
18 #include "sync/syncable/nigori_util.h"
19 #include "sync/syncable/read_transaction.h" 16 #include "sync/syncable/read_transaction.h"
20 #include "sync/syncable/syncable_id.h" 17 #include "sync/syncable/syncable_id.h"
21 #include "sync/syncable/syncable_util.h" 18 #include "sync/syncable/syncable_util.h"
22 #include "sync/syncable/write_transaction.h" 19 #include "sync/syncable/write_transaction.h"
23 #include "sync/test/engine/fake_model_worker.h" 20 #include "sync/test/engine/fake_model_worker.h"
24 #include "sync/test/engine/syncer_command_test.h" 21 #include "sync/test/engine/syncer_command_test.h"
25 #include "sync/test/engine/test_id_factory.h" 22 #include "sync/test/engine/test_id_factory.h"
26 #include "sync/test/fake_encryptor.h"
27 #include "sync/test/fake_sync_encryption_handler.h" 23 #include "sync/test/fake_sync_encryption_handler.h"
28 #include "sync/util/cryptographer.h" 24 #include "sync/util/cryptographer.h"
29 #include "testing/gtest/include/gtest/gtest.h" 25 #include "testing/gtest/include/gtest/gtest.h"
30 26
31 namespace syncer { 27 namespace syncer {
32 28
33 using sessions::SyncSession;
34 using std::string; 29 using std::string;
35 using syncable::Id; 30 using syncable::Id;
36 using syncable::MutableEntry; 31 using syncable::MutableEntry;
37 using syncable::UNITTEST; 32 using syncable::UNITTEST;
38 using syncable::WriteTransaction; 33 using syncable::WriteTransaction;
39 34
40 namespace { 35 namespace {
41 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() { 36 sync_pb::EntitySpecifics DefaultBookmarkSpecifics() {
42 sync_pb::EntitySpecifics result; 37 sync_pb::EntitySpecifics result;
43 AddDefaultFieldValue(BOOKMARKS, &result); 38 AddDefaultFieldValue(BOOKMARKS, &result);
(...skipping 20 matching lines...) Expand all
64 (*mutable_routing_info())[NIGORI] = GROUP_PASSIVE; 59 (*mutable_routing_info())[NIGORI] = GROUP_PASSIVE;
65 SyncerCommandTest::SetUp(); 60 SyncerCommandTest::SetUp();
66 entry_factory_.reset(new TestEntryFactory(directory())); 61 entry_factory_.reset(new TestEntryFactory(directory()));
67 ExpectNoGroupsToChange(apply_updates_command_); 62 ExpectNoGroupsToChange(apply_updates_command_);
68 } 63 }
69 64
70 protected: 65 protected:
71 DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest); 66 DISALLOW_COPY_AND_ASSIGN(ApplyUpdatesCommandTest);
72 67
73 ApplyUpdatesCommand apply_updates_command_; 68 ApplyUpdatesCommand apply_updates_command_;
74 TestIdFactory id_factory_;
75 scoped_ptr<TestEntryFactory> entry_factory_; 69 scoped_ptr<TestEntryFactory> entry_factory_;
76 }; 70 };
77 71
78 TEST_F(ApplyUpdatesCommandTest, Simple) { 72 TEST_F(ApplyUpdatesCommandTest, Simple) {
79 string root_server_id = syncable::GetNullId().GetServerId(); 73 string root_server_id = syncable::GetNullId().GetServerId();
80 entry_factory_->CreateUnappliedNewItemWithParent("parent", 74 entry_factory_->CreateUnappliedNewItemWithParent("parent",
81 DefaultBookmarkSpecifics(), 75 DefaultBookmarkSpecifics(),
82 root_server_id); 76 root_server_id);
83 entry_factory_->CreateUnappliedNewItemWithParent("child", 77 entry_factory_->CreateUnappliedNewItemWithParent("child",
84 DefaultBookmarkSpecifics(), 78 DefaultBookmarkSpecifics(),
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 int64 handle = entry_factory_->CreateUnappliedAndUnsyncedItem( 155 int64 handle = entry_factory_->CreateUnappliedAndUnsyncedItem(
162 "orphaned_by_server", BOOKMARKS); 156 "orphaned_by_server", BOOKMARKS);
163 { 157 {
164 // Manually set the SERVER_PARENT_ID to bad value. 158 // Manually set the SERVER_PARENT_ID to bad value.
165 // A bad parent indicates a hierarchy conflict. 159 // A bad parent indicates a hierarchy conflict.
166 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 160 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
167 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle); 161 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
168 ASSERT_TRUE(entry.good()); 162 ASSERT_TRUE(entry.good());
169 163
170 entry.Put(syncable::SERVER_PARENT_ID, 164 entry.Put(syncable::SERVER_PARENT_ID,
171 id_factory_.MakeServer("bogus_parent")); 165 TestIdFactory::MakeServer("bogus_parent"));
172 } 166 }
173 167
174 ExpectGroupToChange(apply_updates_command_, GROUP_UI); 168 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
175 apply_updates_command_.ExecuteImpl(session()); 169 apply_updates_command_.ExecuteImpl(session());
176 170
177 sessions::StatusController* status = session()->mutable_status_controller(); 171 sessions::StatusController* status = session()->mutable_status_controller();
178 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI); 172 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
179 173
180 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize()); 174 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize());
181 175
(...skipping 16 matching lines...) Expand all
198 // Create it as a child of root node. 192 // Create it as a child of root node.
199 int64 handle = entry_factory_->CreateSyncedItem("X", BOOKMARKS, true); 193 int64 handle = entry_factory_->CreateSyncedItem("X", BOOKMARKS, true);
200 194
201 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 195 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
202 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle); 196 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
203 ASSERT_TRUE(entry.good()); 197 ASSERT_TRUE(entry.good());
204 198
205 // Re-parent from root to "Y" 199 // Re-parent from root to "Y"
206 entry.Put(syncable::SERVER_VERSION, entry_factory_->GetNextRevision()); 200 entry.Put(syncable::SERVER_VERSION, entry_factory_->GetNextRevision());
207 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); 201 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
208 entry.Put(syncable::SERVER_PARENT_ID, id_factory_.MakeServer("Y")); 202 entry.Put(syncable::SERVER_PARENT_ID, TestIdFactory::MakeServer("Y"));
209 } 203 }
210 204
211 // Item 'Y' is child of 'X'. 205 // Item 'Y' is child of 'X'.
212 entry_factory_->CreateUnsyncedItem( 206 entry_factory_->CreateUnsyncedItem(
213 id_factory_.MakeServer("Y"), id_factory_.MakeServer("X"), "Y", true, 207 TestIdFactory::MakeServer("Y"), TestIdFactory::MakeServer("X"), "Y", true,
214 BOOKMARKS, NULL); 208 BOOKMARKS, NULL);
215 209
216 // If the server's update were applied, we would have X be a child of Y, and Y 210 // If the server's update were applied, we would have X be a child of Y, and Y
217 // as a child of X. That's a directory loop. The UpdateApplicator should 211 // as a child of X. That's a directory loop. The UpdateApplicator should
218 // prevent the update from being applied and note that this is a hierarchy 212 // prevent the update from being applied and note that this is a hierarchy
219 // conflict. 213 // conflict.
220 214
221 ExpectGroupToChange(apply_updates_command_, GROUP_UI); 215 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
222 apply_updates_command_.ExecuteImpl(session()); 216 apply_updates_command_.ExecuteImpl(session());
223 217
224 sessions::StatusController* status = session()->mutable_status_controller(); 218 sessions::StatusController* status = session()->mutable_status_controller();
225 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI); 219 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
226 220
227 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize()); 221 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize());
228 222
229 // This should count as a hierarchy conflict. 223 // This should count as a hierarchy conflict.
230 ASSERT_TRUE(status->conflict_progress()); 224 ASSERT_TRUE(status->conflict_progress());
231 EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize()); 225 EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize());
232 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize()); 226 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize());
233 } 227 }
234 228
235 // Runs the ApplyUpdatesCommand on a directory where the server sent us an 229 // Runs the ApplyUpdatesCommand on a directory where the server sent us an
236 // update to add a child to a locally deleted (and unsynced) parent. We expect 230 // update to add a child to a locally deleted (and unsynced) parent. We expect
237 // the command to not apply the update and to indicate the update is in a 231 // the command to not apply the update and to indicate the update is in a
238 // CONFLICT_HIERARCHY state. 232 // CONFLICT_HIERARCHY state.
239 TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDeletedParent) { 233 TEST_F(ApplyUpdatesCommandTest, HierarchyConflictDeletedParent) {
240 // Create a locally deleted parent item. 234 // Create a locally deleted parent item.
241 int64 parent_handle; 235 int64 parent_handle;
242 entry_factory_->CreateUnsyncedItem( 236 entry_factory_->CreateUnsyncedItem(
243 Id::CreateFromServerId("parent"), id_factory_.root(), 237 Id::CreateFromServerId("parent"), TestIdFactory::root(),
244 "parent", true, BOOKMARKS, &parent_handle); 238 "parent", true, BOOKMARKS, &parent_handle);
245 { 239 {
246 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 240 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
247 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, parent_handle); 241 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, parent_handle);
248 entry.Put(syncable::IS_DEL, true); 242 entry.Put(syncable::IS_DEL, true);
249 } 243 }
250 244
251 // Create an incoming child from the server. 245 // Create an incoming child from the server.
252 entry_factory_->CreateUnappliedNewItemWithParent( 246 entry_factory_->CreateUnappliedNewItemWithParent(
253 "child", DefaultBookmarkSpecifics(), "parent"); 247 "child", DefaultBookmarkSpecifics(), "parent");
(...skipping 25 matching lines...) Expand all
279 int64 handle = 273 int64 handle =
280 entry_factory_->CreateSyncedItem("parent", BOOKMARKS, true); 274 entry_factory_->CreateSyncedItem("parent", BOOKMARKS, true);
281 275
282 WriteTransaction trans(FROM_HERE, UNITTEST, directory()); 276 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
283 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle); 277 MutableEntry entry(&trans, syncable::GET_BY_HANDLE, handle);
284 ASSERT_TRUE(entry.good()); 278 ASSERT_TRUE(entry.good());
285 279
286 // Delete it on the server. 280 // Delete it on the server.
287 entry.Put(syncable::SERVER_VERSION, entry_factory_->GetNextRevision()); 281 entry.Put(syncable::SERVER_VERSION, entry_factory_->GetNextRevision());
288 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true); 282 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
289 entry.Put(syncable::SERVER_PARENT_ID, id_factory_.root()); 283 entry.Put(syncable::SERVER_PARENT_ID, TestIdFactory::root());
290 entry.Put(syncable::SERVER_IS_DEL, true); 284 entry.Put(syncable::SERVER_IS_DEL, true);
291 } 285 }
292 286
293 // Create a local child of the server-deleted directory. 287 // Create a local child of the server-deleted directory.
294 entry_factory_->CreateUnsyncedItem( 288 entry_factory_->CreateUnsyncedItem(
295 id_factory_.MakeServer("child"), id_factory_.MakeServer("parent"), 289 TestIdFactory::MakeServer("child"), TestIdFactory::MakeServer("parent"),
296 "child", false, BOOKMARKS, NULL); 290 "child", false, BOOKMARKS, NULL);
297 291
298 // The server's request to delete the directory must be ignored, otherwise our 292 // The server's request to delete the directory must be ignored, otherwise our
299 // unsynced new child would be orphaned. This is a hierarchy conflict. 293 // unsynced new child would be orphaned. This is a hierarchy conflict.
300 294
301 ExpectGroupToChange(apply_updates_command_, GROUP_UI); 295 ExpectGroupToChange(apply_updates_command_, GROUP_UI);
302 apply_updates_command_.ExecuteImpl(session()); 296 apply_updates_command_.ExecuteImpl(session());
303 297
304 sessions::StatusController* status = session()->mutable_status_controller(); 298 sessions::StatusController* status = session()->mutable_status_controller();
305 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI); 299 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_UI);
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
508 << "The updates that can't be decrypted should not be in regular " 502 << "The updates that can't be decrypted should not be in regular "
509 << "conflict"; 503 << "conflict";
510 EXPECT_EQ(1, status->conflict_progress()->EncryptionConflictingItemsSize()) 504 EXPECT_EQ(1, status->conflict_progress()->EncryptionConflictingItemsSize())
511 << "The updates that can't be decrypted should be in encryption " 505 << "The updates that can't be decrypted should be in encryption "
512 << "conflict"; 506 << "conflict";
513 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount()) 507 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
514 << "The undecryptable password update shouldn't be applied"; 508 << "The undecryptable password update shouldn't be applied";
515 } 509 }
516 } 510 }
517 511
518 TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
519 // Storing the cryptographer separately is bad, but for this test we
520 // know it's safe.
521 Cryptographer* cryptographer;
522 ModelTypeSet encrypted_types;
523 encrypted_types.Put(PASSWORDS);
524 encrypted_types.Put(NIGORI);
525 {
526 syncable::ReadTransaction trans(FROM_HERE, directory());
527 cryptographer = directory()->GetCryptographer(&trans);
528 EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
529 .Equals(encrypted_types));
530 }
531
532 // Nigori node updates should update the Cryptographer.
533 Cryptographer other_cryptographer(cryptographer->encryptor());
534 KeyParams params = {"localhost", "dummy", "foobar"};
535 other_cryptographer.AddKey(params);
536
537 sync_pb::EntitySpecifics specifics;
538 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
539 other_cryptographer.GetKeys(nigori->mutable_encrypted());
540 nigori->set_encrypt_everything(true);
541 entry_factory_->CreateUnappliedNewItem(
542 ModelTypeToRootTag(NIGORI), specifics, true);
543 EXPECT_FALSE(cryptographer->has_pending_keys());
544
545 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
546 apply_updates_command_.ExecuteImpl(session());
547
548 sessions::StatusController* status = session()->mutable_status_controller();
549 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
550 ASSERT_TRUE(status->update_progress());
551 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
552 << "All updates should have been attempted";
553 ASSERT_TRUE(status->conflict_progress());
554 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
555 << "The nigori update shouldn't be in conflict";
556 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
557 << "The nigori update should be applied";
558
559 EXPECT_FALSE(cryptographer->is_ready());
560 EXPECT_TRUE(cryptographer->has_pending_keys());
561 {
562 syncable::ReadTransaction trans(FROM_HERE, directory());
563 EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
564 .Equals(ModelTypeSet::All()));
565 }
566 }
567
568 TEST_F(ApplyUpdatesCommandTest, NigoriUpdateForDisabledTypes) {
569 // Storing the cryptographer separately is bad, but for this test we
570 // know it's safe.
571 Cryptographer* cryptographer;
572 ModelTypeSet encrypted_types;
573 encrypted_types.Put(PASSWORDS);
574 encrypted_types.Put(NIGORI);
575 {
576 syncable::ReadTransaction trans(FROM_HERE, directory());
577 cryptographer = directory()->GetCryptographer(&trans);
578 EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
579 .Equals(encrypted_types));
580 }
581
582 // Nigori node updates should update the Cryptographer.
583 Cryptographer other_cryptographer(cryptographer->encryptor());
584 KeyParams params = {"localhost", "dummy", "foobar"};
585 other_cryptographer.AddKey(params);
586
587 sync_pb::EntitySpecifics specifics;
588 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
589 other_cryptographer.GetKeys(nigori->mutable_encrypted());
590 nigori->set_encrypt_everything(true);
591 entry_factory_->CreateUnappliedNewItem(
592 ModelTypeToRootTag(NIGORI), specifics, true);
593 EXPECT_FALSE(cryptographer->has_pending_keys());
594
595 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
596 apply_updates_command_.ExecuteImpl(session());
597
598 sessions::StatusController* status = session()->mutable_status_controller();
599 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
600 ASSERT_TRUE(status->update_progress());
601 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
602 << "All updates should have been attempted";
603 ASSERT_TRUE(status->conflict_progress());
604 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
605 << "The nigori update shouldn't be in conflict";
606 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
607 << "The nigori update should be applied";
608
609 EXPECT_FALSE(cryptographer->is_ready());
610 EXPECT_TRUE(cryptographer->has_pending_keys());
611 {
612 syncable::ReadTransaction trans(FROM_HERE, directory());
613 EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
614 .Equals(ModelTypeSet::All()));
615 }
616 }
617
618 // Create some local unsynced and unencrypted data. Apply a nigori update that
619 // turns on encryption for the unsynced data. Ensure we properly encrypt the
620 // data as part of the nigori update. Apply another nigori update with no
621 // changes. Ensure we ignore already-encrypted unsynced data and that nothing
622 // breaks.
623 TEST_F(ApplyUpdatesCommandTest, EncryptUnsyncedChanges) {
624 // Storing the cryptographer separately is bad, but for this test we
625 // know it's safe.
626 Cryptographer* cryptographer;
627 ModelTypeSet encrypted_types;
628 encrypted_types.Put(PASSWORDS);
629 encrypted_types.Put(NIGORI);
630 {
631 syncable::ReadTransaction trans(FROM_HERE, directory());
632 cryptographer = directory()->GetCryptographer(&trans);
633 EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
634 .Equals(encrypted_types));
635
636 // With default encrypted_types, this should be true.
637 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
638
639 Syncer::UnsyncedMetaHandles handles;
640 GetUnsyncedEntries(&trans, &handles);
641 EXPECT_TRUE(handles.empty());
642 }
643
644 // Create unsynced bookmarks without encryption.
645 // First item is a folder
646 Id folder_id = id_factory_.NewLocalId();
647 entry_factory_->CreateUnsyncedItem(folder_id, id_factory_.root(), "folder",
648 true, BOOKMARKS, NULL);
649 // Next five items are children of the folder
650 size_t i;
651 size_t batch_s = 5;
652 for (i = 0; i < batch_s; ++i) {
653 entry_factory_->CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
654 base::StringPrintf("Item %"PRIuS"", i),
655 false, BOOKMARKS, NULL);
656 }
657 // Next five items are children of the root.
658 for (; i < 2*batch_s; ++i) {
659 entry_factory_->CreateUnsyncedItem(
660 id_factory_.NewLocalId(), id_factory_.root(),
661 base::StringPrintf("Item %"PRIuS"", i), false,
662 BOOKMARKS, NULL);
663 }
664
665 KeyParams params = {"localhost", "dummy", "foobar"};
666 cryptographer->AddKey(params);
667 sync_pb::EntitySpecifics specifics;
668 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
669 cryptographer->GetKeys(nigori->mutable_encrypted());
670 nigori->set_encrypt_everything(true);
671 encrypted_types.Put(BOOKMARKS);
672 entry_factory_->CreateUnappliedNewItem(
673 ModelTypeToRootTag(NIGORI), specifics, true);
674 EXPECT_FALSE(cryptographer->has_pending_keys());
675 EXPECT_TRUE(cryptographer->is_ready());
676
677 {
678 // Ensure we have unsynced nodes that aren't properly encrypted.
679 syncable::ReadTransaction trans(FROM_HERE, directory());
680 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
681
682 Syncer::UnsyncedMetaHandles handles;
683 GetUnsyncedEntries(&trans, &handles);
684 EXPECT_EQ(2*batch_s+1, handles.size());
685 }
686
687 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
688 apply_updates_command_.ExecuteImpl(session());
689
690 {
691 sessions::StatusController* status = session()->mutable_status_controller();
692 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
693 ASSERT_TRUE(status->update_progress());
694 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
695 << "All updates should have been attempted";
696 ASSERT_TRUE(status->conflict_progress());
697 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
698 << "No updates should be in conflict";
699 EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
700 << "No updates should be in conflict";
701 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
702 << "The nigori update should be applied";
703 }
704 EXPECT_FALSE(cryptographer->has_pending_keys());
705 EXPECT_TRUE(cryptographer->is_ready());
706 {
707 syncable::ReadTransaction trans(FROM_HERE, directory());
708
709 // If ProcessUnsyncedChangesForEncryption worked, all our unsynced changes
710 // should be encrypted now.
711 EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
712 .Equals(ModelTypeSet::All()));
713 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
714
715 Syncer::UnsyncedMetaHandles handles;
716 GetUnsyncedEntries(&trans, &handles);
717 EXPECT_EQ(2*batch_s+1, handles.size());
718 }
719
720 // Simulate another nigori update that doesn't change anything.
721 {
722 WriteTransaction trans(FROM_HERE, UNITTEST, directory());
723 MutableEntry entry(&trans, syncable::GET_BY_SERVER_TAG,
724 ModelTypeToRootTag(NIGORI));
725 ASSERT_TRUE(entry.good());
726 entry.Put(syncable::SERVER_VERSION, entry_factory_->GetNextRevision());
727 entry.Put(syncable::IS_UNAPPLIED_UPDATE, true);
728 }
729 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
730 apply_updates_command_.ExecuteImpl(session());
731 {
732 sessions::StatusController* status = session()->mutable_status_controller();
733 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
734 ASSERT_TRUE(status->update_progress());
735 EXPECT_EQ(2, status->update_progress()->AppliedUpdatesSize())
736 << "All updates should have been attempted";
737 ASSERT_TRUE(status->conflict_progress());
738 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
739 << "No updates should be in conflict";
740 EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
741 << "No updates should be in conflict";
742 EXPECT_EQ(2, status->update_progress()->SuccessfullyAppliedUpdateCount())
743 << "The nigori update should be applied";
744 }
745 EXPECT_FALSE(cryptographer->has_pending_keys());
746 EXPECT_TRUE(cryptographer->is_ready());
747 {
748 syncable::ReadTransaction trans(FROM_HERE, directory());
749
750 // All our changes should still be encrypted.
751 EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
752 .Equals(ModelTypeSet::All()));
753 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
754
755 Syncer::UnsyncedMetaHandles handles;
756 GetUnsyncedEntries(&trans, &handles);
757 EXPECT_EQ(2*batch_s+1, handles.size());
758 }
759 }
760
761 TEST_F(ApplyUpdatesCommandTest, CannotEncryptUnsyncedChanges) {
762 // Storing the cryptographer separately is bad, but for this test we
763 // know it's safe.
764 Cryptographer* cryptographer;
765 ModelTypeSet encrypted_types;
766 encrypted_types.Put(PASSWORDS);
767 encrypted_types.Put(NIGORI);
768 {
769 syncable::ReadTransaction trans(FROM_HERE, directory());
770 cryptographer = directory()->GetCryptographer(&trans);
771 EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
772 .Equals(encrypted_types));
773
774 // With default encrypted_types, this should be true.
775 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
776
777 Syncer::UnsyncedMetaHandles handles;
778 GetUnsyncedEntries(&trans, &handles);
779 EXPECT_TRUE(handles.empty());
780 }
781
782 // Create unsynced bookmarks without encryption.
783 // First item is a folder
784 Id folder_id = id_factory_.NewLocalId();
785 entry_factory_->CreateUnsyncedItem(
786 folder_id, id_factory_.root(), "folder", true,
787 BOOKMARKS, NULL);
788 // Next five items are children of the folder
789 size_t i;
790 size_t batch_s = 5;
791 for (i = 0; i < batch_s; ++i) {
792 entry_factory_->CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
793 base::StringPrintf("Item %"PRIuS"", i),
794 false, BOOKMARKS, NULL);
795 }
796 // Next five items are children of the root.
797 for (; i < 2*batch_s; ++i) {
798 entry_factory_->CreateUnsyncedItem(
799 id_factory_.NewLocalId(), id_factory_.root(),
800 base::StringPrintf("Item %"PRIuS"", i), false,
801 BOOKMARKS, NULL);
802 }
803
804 // We encrypt with new keys, triggering the local cryptographer to be unready
805 // and unable to decrypt data (once updated).
806 Cryptographer other_cryptographer(cryptographer->encryptor());
807 KeyParams params = {"localhost", "dummy", "foobar"};
808 other_cryptographer.AddKey(params);
809 sync_pb::EntitySpecifics specifics;
810 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
811 other_cryptographer.GetKeys(nigori->mutable_encrypted());
812 nigori->set_encrypt_everything(true);
813 encrypted_types.Put(BOOKMARKS);
814 entry_factory_->CreateUnappliedNewItem(
815 ModelTypeToRootTag(NIGORI), specifics, true);
816 EXPECT_FALSE(cryptographer->has_pending_keys());
817
818 {
819 // Ensure we have unsynced nodes that aren't properly encrypted.
820 syncable::ReadTransaction trans(FROM_HERE, directory());
821 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
822 Syncer::UnsyncedMetaHandles handles;
823 GetUnsyncedEntries(&trans, &handles);
824 EXPECT_EQ(2*batch_s+1, handles.size());
825 }
826
827 ExpectGroupToChange(apply_updates_command_, GROUP_PASSIVE);
828 apply_updates_command_.ExecuteImpl(session());
829
830 sessions::StatusController* status = session()->mutable_status_controller();
831 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE);
832 ASSERT_TRUE(status->update_progress());
833 EXPECT_EQ(1, status->update_progress()->AppliedUpdatesSize())
834 << "All updates should have been attempted";
835 ASSERT_TRUE(status->conflict_progress());
836 EXPECT_EQ(0, status->conflict_progress()->SimpleConflictingItemsSize())
837 << "The unsynced changes don't trigger a blocking conflict with the "
838 << "nigori update.";
839 EXPECT_EQ(0, status->conflict_progress()->EncryptionConflictingItemsSize())
840 << "The unsynced changes don't trigger an encryption conflict with the "
841 << "nigori update.";
842 EXPECT_EQ(1, status->update_progress()->SuccessfullyAppliedUpdateCount())
843 << "The nigori update should be applied";
844 EXPECT_FALSE(cryptographer->is_ready());
845 EXPECT_TRUE(cryptographer->has_pending_keys());
846 {
847 syncable::ReadTransaction trans(FROM_HERE, directory());
848
849 // Since we have pending keys, we would have failed to encrypt, but the
850 // cryptographer should be updated.
851 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
852 EXPECT_TRUE(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans)
853 .Equals(ModelTypeSet::All()));
854 EXPECT_FALSE(cryptographer->is_ready());
855 EXPECT_TRUE(cryptographer->has_pending_keys());
856
857 Syncer::UnsyncedMetaHandles handles;
858 GetUnsyncedEntries(&trans, &handles);
859 EXPECT_EQ(2*batch_s+1, handles.size());
860 }
861 }
862
863 } // namespace syncer 512 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698