| Index: chrome/browser/sync/internal_api/syncapi_unittest.cc
|
| diff --git a/chrome/browser/sync/internal_api/syncapi_unittest.cc b/chrome/browser/sync/internal_api/syncapi_unittest.cc
|
| index 6e3c296963837f23388ea3cb4d357eb60592b60a..66a71b9494619b107bf63193b5b5bbf9068e97ad 100644
|
| --- a/chrome/browser/sync/internal_api/syncapi_unittest.cc
|
| +++ b/chrome/browser/sync/internal_api/syncapi_unittest.cc
|
| @@ -73,9 +73,13 @@ using browser_sync::sessions::SyncSessionSnapshot;
|
| using browser_sync::WeakHandle;
|
| using content::BrowserThread;
|
| using syncable::GetAllRealModelTypes;
|
| +using syncable::IS_DEL;
|
| +using syncable::IS_UNSYNCED;
|
| using syncable::kEncryptedString;
|
| using syncable::ModelType;
|
| using syncable::ModelTypeSet;
|
| +using syncable::NON_UNIQUE_NAME;
|
| +using syncable::SPECIFICS;
|
| using test::ExpectDictStringValue;
|
| using testing::_;
|
| using testing::AnyNumber;
|
| @@ -179,6 +183,37 @@ int64 MakeServerNodeForType(UserShare* share,
|
| return entry.Get(syncable::META_HANDLE);
|
| }
|
|
|
| +// Simulates creating a "synced" node as a child of the root datatype node.
|
| +int64 MakeServerNode(UserShare* share, ModelType model_type,
|
| + const std::string& client_tag,
|
| + const std::string& hashed_tag,
|
| + const sync_pb::EntitySpecifics& specifics) {
|
| + syncable::ScopedDirLookup dir(share->dir_manager.get(), share->name);
|
| + EXPECT_TRUE(dir.good());
|
| + syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir);
|
| + syncable::Entry root_entry(&trans, syncable::GET_BY_SERVER_TAG,
|
| + syncable::ModelTypeToRootTag(model_type));
|
| + EXPECT_TRUE(root_entry.good());
|
| + syncable::Id root_id = root_entry.Get(syncable::ID);
|
| + syncable::Id node_id = syncable::Id::CreateFromServerId(client_tag);
|
| + syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
|
| + node_id);
|
| + EXPECT_TRUE(entry.good());
|
| + entry.Put(syncable::BASE_VERSION, 1);
|
| + entry.Put(syncable::SERVER_VERSION, 1);
|
| + entry.Put(syncable::IS_UNAPPLIED_UPDATE, false);
|
| + entry.Put(syncable::SERVER_PARENT_ID, root_id);
|
| + entry.Put(syncable::PARENT_ID, root_id);
|
| + entry.Put(syncable::SERVER_IS_DIR, false);
|
| + entry.Put(syncable::IS_DIR, false);
|
| + entry.Put(syncable::SERVER_SPECIFICS, specifics);
|
| + entry.Put(syncable::NON_UNIQUE_NAME, client_tag);
|
| + entry.Put(syncable::UNIQUE_CLIENT_TAG, hashed_tag);
|
| + entry.Put(syncable::IS_DEL, false);
|
| + entry.Put(syncable::SPECIFICS, specifics);
|
| + return entry.Get(syncable::META_HANDLE);
|
| +}
|
| +
|
| } // namespace
|
|
|
| class SyncApiTest : public testing::Test {
|
| @@ -651,6 +686,8 @@ class SyncNotifierMock : public sync_notifier::SyncNotifier {
|
| MOCK_METHOD1(SendNotification, void(const syncable::ModelTypeSet&));
|
| };
|
|
|
| +} // namespace
|
| +
|
| class SyncManagerTest : public testing::Test,
|
| public ModelSafeWorkerRegistrar,
|
| public SyncManager::ChangeDelegate {
|
| @@ -747,7 +784,7 @@ class SyncManagerTest : public testing::Test,
|
| virtual void OnChangesComplete(syncable::ModelType model_type) OVERRIDE {}
|
|
|
| // Helper methods.
|
| - bool SetUpEncryption(bool write_to_nigori) {
|
| + bool SetUpEncryption(bool write_to_nigori, bool encrypt_everything) {
|
| // Mock the Mac Keychain service. The real Keychain can block on user input.
|
| #if defined(OS_MACOSX)
|
| Encryptor::UseMockKeychain(true);
|
| @@ -773,9 +810,12 @@ class SyncManagerTest : public testing::Test,
|
| return false;
|
| KeyParams params = {"localhost", "dummy", "foobar"};
|
| cryptographer->AddKey(params);
|
| + if (encrypt_everything)
|
| + cryptographer->set_encrypt_everything();
|
| if (write_to_nigori) {
|
| sync_pb::NigoriSpecifics nigori;
|
| cryptographer->GetKeys(nigori.mutable_encrypted());
|
| + cryptographer->UpdateNigoriFromEncryptedTypes(&nigori);
|
| WriteNode node(&trans);
|
| EXPECT_TRUE(node.InitByIdLookup(nigori_id));
|
| node.SetNigoriSpecifics(nigori);
|
| @@ -831,6 +871,26 @@ class SyncManagerTest : public testing::Test,
|
| PumpLoop();
|
| }
|
|
|
| + // Looks up an entry by client tag and resets IS_UNSYNCED value to false.
|
| + // Returns true if entry was previously unsynced, false if IS_UNSYNCED was
|
| + // already false.
|
| + bool ResetUnsyncedEntry(syncable::ModelType type,
|
| + const std::string& client_tag) {
|
| + UserShare* share = sync_manager_.GetUserShare();
|
| + syncable::ScopedDirLookup dir(share->dir_manager.get(), share->name);
|
| + EXPECT_TRUE(dir.good());
|
| + syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir);
|
| + const std::string hash = BaseNode::GenerateSyncableHash(type, client_tag);
|
| + syncable::MutableEntry entry(&trans, syncable::GET_BY_CLIENT_TAG,
|
| + hash);
|
| + EXPECT_TRUE(entry.good());
|
| + if (!entry.Get(IS_UNSYNCED))
|
| + return false;
|
| + else
|
| + entry.Put(IS_UNSYNCED, false);
|
| + return true;
|
| + }
|
| +
|
| private:
|
| // Needed by |ui_thread_|.
|
| MessageLoopForUI ui_loop_;
|
| @@ -1205,7 +1265,7 @@ TEST_F(SyncManagerTest, OnIncomingNotification) {
|
| }
|
|
|
| TEST_F(SyncManagerTest, RefreshEncryptionReady) {
|
| - EXPECT_TRUE(SetUpEncryption(true));
|
| + EXPECT_TRUE(SetUpEncryption(true, false));
|
| EXPECT_CALL(observer_, OnEncryptionComplete());
|
| sync_manager_.RefreshEncryption();
|
| syncable::ModelTypeSet encrypted_types =
|
| @@ -1236,7 +1296,7 @@ TEST_F(SyncManagerTest, RefreshEncryptionNotReady) {
|
|
|
| // Attempt to refresh encryption when nigori is empty.
|
| TEST_F(SyncManagerTest, RefreshEncryptionEmptyNigori) {
|
| - EXPECT_TRUE(SetUpEncryption(false));
|
| + EXPECT_TRUE(SetUpEncryption(false, false));
|
| EXPECT_CALL(observer_, OnEncryptionComplete());
|
| sync_manager_.RefreshEncryption(); // Should write to nigori.
|
| syncable::ModelTypeSet encrypted_types =
|
| @@ -1256,7 +1316,7 @@ TEST_F(SyncManagerTest, RefreshEncryptionEmptyNigori) {
|
| }
|
|
|
| TEST_F(SyncManagerTest, EncryptDataTypesWithNoData) {
|
| - EXPECT_TRUE(SetUpEncryption(true));
|
| + EXPECT_TRUE(SetUpEncryption(true, false));
|
| EXPECT_CALL(observer_,
|
| OnEncryptedTypesChanged(GetAllRealModelTypes(), true));
|
| EXPECT_CALL(observer_, OnEncryptionComplete());
|
| @@ -1266,7 +1326,7 @@ TEST_F(SyncManagerTest, EncryptDataTypesWithNoData) {
|
|
|
| TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
|
| size_t batch_size = 5;
|
| - EXPECT_TRUE(SetUpEncryption(true));
|
| + EXPECT_TRUE(SetUpEncryption(true, false));
|
|
|
| // Create some unencrypted unsynced data.
|
| int64 folder = MakeFolderWithParent(sync_manager_.GetUserShare(),
|
| @@ -1372,7 +1432,7 @@ TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
|
| }
|
|
|
| TEST_F(SyncManagerTest, SetPassphraseWithPassword) {
|
| - EXPECT_TRUE(SetUpEncryption(true));
|
| + EXPECT_TRUE(SetUpEncryption(true, false));
|
| {
|
| WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
|
| ReadNode root_node(&trans);
|
| @@ -1401,7 +1461,7 @@ TEST_F(SyncManagerTest, SetPassphraseWithPassword) {
|
| }
|
|
|
| TEST_F(SyncManagerTest, SetPassphraseWithEmptyPasswordNode) {
|
| - EXPECT_TRUE(SetUpEncryption(true));
|
| + EXPECT_TRUE(SetUpEncryption(true, false));
|
| int64 node_id = 0;
|
| std::string tag = "foo";
|
| {
|
| @@ -1431,11 +1491,9 @@ TEST_F(SyncManagerTest, SetPassphraseWithEmptyPasswordNode) {
|
| }
|
| }
|
|
|
| -} // namespace
|
| -
|
| // Friended by WriteNode, so can't be in an anonymouse namespace.
|
| TEST_F(SyncManagerTest, EncryptBookmarksWithLegacyData) {
|
| - EXPECT_TRUE(SetUpEncryption(true));
|
| + EXPECT_TRUE(SetUpEncryption(true, false));
|
| std::string title;
|
| SyncAPINameToServerName("Google", &title);
|
| std::string url = "http://www.google.com";
|
| @@ -1537,4 +1595,116 @@ TEST_F(SyncManagerTest, EncryptBookmarksWithLegacyData) {
|
| }
|
| }
|
|
|
| +// Verifies WriteNode::UpdateEntryWithEncryption does not make unnecessary
|
| +// changes.
|
| +TEST_F(SyncManagerTest, UpdateEntryWithEncryption) {
|
| + std::string client_tag = "title";
|
| + sync_pb::EntitySpecifics entity_specifics;
|
| + entity_specifics.MutableExtension(sync_pb::bookmark)->set_url("url");
|
| + entity_specifics.MutableExtension(sync_pb::bookmark)->set_title("title");
|
| + MakeServerNode(sync_manager_.GetUserShare(), syncable::BOOKMARKS, client_tag,
|
| + BaseNode::GenerateSyncableHash(syncable::BOOKMARKS,
|
| + client_tag),
|
| + entity_specifics);
|
| + // New node shouldn't start off unsynced.
|
| + EXPECT_FALSE(ResetUnsyncedEntry(syncable::BOOKMARKS, client_tag));
|
| + // Manually change to the same data. Should not set is_unsynced.
|
| + {
|
| + WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
|
| + WriteNode node(&trans);
|
| + EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS, client_tag));
|
| + node.SetEntitySpecifics(entity_specifics);
|
| + }
|
| + EXPECT_FALSE(ResetUnsyncedEntry(syncable::BOOKMARKS, client_tag));
|
| + // Encrypt the datatatype, should set is_unsynced.
|
| + EXPECT_CALL(observer_,
|
| + OnEncryptedTypesChanged(GetAllRealModelTypes(), true));
|
| + EXPECT_CALL(observer_, OnEncryptionComplete());
|
| + EXPECT_TRUE(SetUpEncryption(true, true));
|
| + sync_manager_.RefreshEncryption();
|
| + {
|
| + ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
|
| + ReadNode node(&trans);
|
| + EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS, client_tag));
|
| + const syncable::Entry* node_entry = node.GetEntry();
|
| + const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS);
|
| + EXPECT_TRUE(specifics.has_encrypted());
|
| + EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME));
|
| + Cryptographer* cryptographer = trans.GetCryptographer();
|
| + EXPECT_TRUE(cryptographer->is_ready());
|
| + EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
|
| + specifics.encrypted()));
|
| + }
|
| + EXPECT_TRUE(ResetUnsyncedEntry(syncable::BOOKMARKS, client_tag));
|
| + // Set a new passphrase. Should set is_unsynced.
|
| + testing::Mock::VerifyAndClearExpectations(&observer_);
|
| + EXPECT_CALL(observer_, OnPassphraseAccepted(_));
|
| + EXPECT_CALL(observer_, OnEncryptionComplete());
|
| + sync_manager_.SetPassphrase("new_passphrase", true);
|
| + {
|
| + ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
|
| + ReadNode node(&trans);
|
| + EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS, client_tag));
|
| + const syncable::Entry* node_entry = node.GetEntry();
|
| + const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS);
|
| + EXPECT_TRUE(specifics.has_encrypted());
|
| + EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME));
|
| + Cryptographer* cryptographer = trans.GetCryptographer();
|
| + EXPECT_TRUE(cryptographer->is_ready());
|
| + EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
|
| + specifics.encrypted()));
|
| + }
|
| + EXPECT_TRUE(ResetUnsyncedEntry(syncable::BOOKMARKS, client_tag));
|
| + // Force a re-encrypt everything. Should not set is_unsynced.
|
| + testing::Mock::VerifyAndClearExpectations(&observer_);
|
| + EXPECT_CALL(observer_, OnEncryptionComplete());
|
| + sync_manager_.RefreshEncryption();
|
| + {
|
| + ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
|
| + ReadNode node(&trans);
|
| + EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS, client_tag));
|
| + const syncable::Entry* node_entry = node.GetEntry();
|
| + const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS);
|
| + EXPECT_TRUE(specifics.has_encrypted());
|
| + EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME));
|
| + Cryptographer* cryptographer = trans.GetCryptographer();
|
| + EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
|
| + specifics.encrypted()));
|
| + }
|
| + EXPECT_FALSE(ResetUnsyncedEntry(syncable::BOOKMARKS, client_tag));
|
| + // Manually change to the same data. Should not set is_unsynced.
|
| + {
|
| + WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
|
| + WriteNode node(&trans);
|
| + EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS, client_tag));
|
| + node.SetEntitySpecifics(entity_specifics);
|
| + const syncable::Entry* node_entry = node.GetEntry();
|
| + const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS);
|
| + EXPECT_TRUE(specifics.has_encrypted());
|
| + EXPECT_FALSE(node_entry->Get(IS_UNSYNCED));
|
| + EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME));
|
| + Cryptographer* cryptographer = trans.GetCryptographer();
|
| + EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
|
| + specifics.encrypted()));
|
| + }
|
| + EXPECT_FALSE(ResetUnsyncedEntry(syncable::BOOKMARKS, client_tag));
|
| + // Manually change to different data. Should set is_unsynced.
|
| + {
|
| + entity_specifics.MutableExtension(sync_pb::bookmark)->set_url("url2");
|
| + entity_specifics.MutableExtension(sync_pb::bookmark)->set_title("title2");
|
| + WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare());
|
| + WriteNode node(&trans);
|
| + EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS, client_tag));
|
| + node.SetEntitySpecifics(entity_specifics);
|
| + const syncable::Entry* node_entry = node.GetEntry();
|
| + const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS);
|
| + EXPECT_TRUE(specifics.has_encrypted());
|
| + EXPECT_TRUE(node_entry->Get(IS_UNSYNCED));
|
| + EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME));
|
| + Cryptographer* cryptographer = trans.GetCryptographer();
|
| + EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
|
| + specifics.encrypted()));
|
| + }
|
| +}
|
| +
|
| } // namespace browser_sync
|
|
|