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

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

Powered by Google App Engine
This is Rietveld 408576698