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 |