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

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

Powered by Google App Engine
This is Rietveld 408576698