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

Unified Diff: chrome/browser/sync/engine/syncapi_unittest.cc

Issue 6465005: [Sync] Initial support for encrypting any datatype (no UI hookup yet). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comments. Rest of unit tests. Created 9 years, 10 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/sync/engine/syncapi_unittest.cc
diff --git a/chrome/browser/sync/engine/syncapi_unittest.cc b/chrome/browser/sync/engine/syncapi_unittest.cc
index f4a05a5f99781e044f2bb1a4f105f70b70ab85cb..fb5537160a19874e8a7afb36e8dc76c92e2435ec 100644
--- a/chrome/browser/sync/engine/syncapi_unittest.cc
+++ b/chrome/browser/sync/engine/syncapi_unittest.cc
@@ -6,14 +6,18 @@
// functionality is provided by the Syncable layer, which has its own
// unit tests. We'll test SyncApi specific things in this harness.
+#include <map>
+
#include "base/basictypes.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
#include "base/scoped_temp_dir.h"
#include "base/string_number_conversions.h"
+#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/sync/engine/model_safe_worker.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/js_arg_list.h"
#include "chrome/browser/sync/js_backend.h"
@@ -22,24 +26,110 @@
#include "chrome/browser/sync/js_test_util.h"
#include "chrome/browser/sync/protocol/password_specifics.pb.h"
#include "chrome/browser/sync/protocol/proto_value_conversions.h"
+#include "chrome/browser/sync/sessions/sync_session.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/nigori_util.h"
#include "chrome/browser/sync/syncable/syncable.h"
+#include "chrome/browser/sync/syncable/syncable_id.h"
+#include "chrome/browser/sync/util/cryptographer.h"
#include "chrome/test/sync/engine/test_directory_setter_upper.h"
#include "jingle/notifier/base/notifier_options.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using browser_sync::Cryptographer;
using browser_sync::HasArgsAsList;
using browser_sync::KeyParams;
using browser_sync::JsArgList;
using browser_sync::MockJsEventHandler;
using browser_sync::MockJsEventRouter;
+using browser_sync::ModelSafeRoutingInfo;
+using browser_sync::ModelSafeWorker;
+using browser_sync::ModelSafeWorkerRegistrar;
+using browser_sync::sessions::SyncSessionSnapshot;
+using syncable::ModelType;
+using syncable::ModelTypeSet;
using testing::_;
using testing::SaveArg;
using testing::StrictMock;
namespace sync_api {
+namespace {
+
+// Utility methods for creating nodes through the syncapi.
+int64 MakeNode(UserShare* share,
+ ModelType model_type,
+ const std::string& client_tag) {
+ WriteTransaction trans(share);
+ ReadNode root_node(&trans);
+ root_node.InitByRootLookup();
+ WriteNode node(&trans);
+ EXPECT_TRUE(node.InitUniqueByCreation(model_type, root_node, client_tag));
+ node.SetIsFolder(false);
+ return node.GetId();
+}
+
+int64 MakeNodeWithParent(UserShare* share,
+ ModelType model_type,
+ const std::string& client_tag,
+ int64 parent_id) {
+ WriteTransaction trans(share);
+ ReadNode parent_node(&trans);
+ parent_node.InitByIdLookup(parent_id);
+ WriteNode node(&trans);
+ EXPECT_TRUE(node.InitUniqueByCreation(model_type, parent_node, client_tag));
+ node.SetIsFolder(false);
+
+ return node.GetId();
+}
+
+int64 MakeFolderWithParent(UserShare* share,
+ ModelType model_type,
+ int64 parent_id,
+ BaseNode* predecessor) {
+ WriteTransaction trans(share);
+ ReadNode parent_node(&trans);
+ parent_node.InitByIdLookup(parent_id);
+ WriteNode node(&trans);
+ EXPECT_TRUE(node.InitByCreation(model_type, parent_node, predecessor));
+ node.SetIsFolder(true);
+ return node.GetId();
+}
+
+// Creates the "synced" root node for a particular datatype. We use the syncable
+// methods here so that the syncer treats these nodes as if they were already
+// received from the server.
+int64 MakeServerNodeForType(UserShare* share,
+ ModelType model_type) {
+ sync_pb::EntitySpecifics specifics;
+ syncable::AddDefaultExtensionValue(model_type, &specifics);
+ syncable::ScopedDirLookup dir(share->dir_manager.get(), share->name);
+ if (!dir.good())
+ return 0;
+ syncable::WriteTransaction trans(dir, syncable::UNITTEST, __FILE__, __LINE__);
+ // Attempt to lookup by nigori tag.
+ std::string type_tag = syncable::ModelTypeToRootTag(model_type);
+ syncable::Id node_id = syncable::Id::CreateFromServerId(type_tag);
+ syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM,
+ node_id);
+ if (!entry.good())
+ return 0;
+ 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, syncable::kNullId);
+ entry.Put(syncable::SERVER_IS_DIR, false);
+ entry.Put(syncable::SERVER_SPECIFICS, specifics);
+ entry.Put(syncable::UNIQUE_SERVER_TAG, type_tag);
+ entry.Put(syncable::NON_UNIQUE_NAME, type_tag);
+ entry.Put(syncable::IS_DEL, false);
+ entry.Put(syncable::SPECIFICS, specifics);
+ return entry.Get(syncable::META_HANDLE);
+}
+
+} // namespace
+
class SyncApiTest : public testing::Test {
public:
virtual void SetUp() {
@@ -310,11 +400,11 @@ void CheckNodeValue(const BaseNode& node, const DictionaryValue& value) {
EXPECT_EQ(node.GetTitle(), UTF8ToWide(title));
}
{
- syncable::ModelType expected_model_type = node.GetModelType();
+ ModelType expected_model_type = node.GetModelType();
std::string type_str;
EXPECT_TRUE(value.GetString("type", &type_str));
if (expected_model_type >= syncable::FIRST_REAL_MODEL_TYPE) {
- syncable::ModelType model_type =
+ ModelType model_type =
syncable::ModelTypeFromString(type_str);
EXPECT_EQ(expected_model_type, model_type);
} else if (expected_model_type == syncable::TOP_LEVEL_FOLDER) {
@@ -368,22 +458,101 @@ class TestHttpPostProviderFactory : public HttpPostProviderFactory {
}
};
-class SyncManagerTest : public testing::Test {
+class SyncManagerObserverMock : public SyncManager::Observer {
+ public:
+ MOCK_METHOD4(OnChangesApplied,
+ void(ModelType,
+ const BaseTransaction*,
+ const SyncManager::ChangeRecord*,
+ int));
+ MOCK_METHOD1(OnChangesComplete, void(ModelType));
+ MOCK_METHOD1(OnSyncCycleCompleted,
+ void(const SyncSessionSnapshot*));
+ MOCK_METHOD0(OnInitializationComplete, void());
+ MOCK_METHOD1(OnAuthError, void(const GoogleServiceAuthError&));
+ MOCK_METHOD1(OnPassphraseRequired, void(bool));
+ MOCK_METHOD1(OnPassphraseAccepted, void(const std::string&));
+ MOCK_METHOD0(OnPaused, void());
+ MOCK_METHOD0(OnResumed, void());
+ MOCK_METHOD0(OnStopSyncingPermanently, void());
+ MOCK_METHOD1(OnUpdatedToken, void(const std::string&));
+ MOCK_METHOD0(OnClearServerDataFailed, void());
+ MOCK_METHOD0(OnClearServerDataSucceeded, void());
+ MOCK_METHOD1(OnEncryptionComplete, void(const ModelTypeSet&));
+};
+
+class SyncManagerTest : public testing::Test,
+ public ModelSafeWorkerRegistrar {
protected:
SyncManagerTest() : ui_thread_(BrowserThread::UI, &ui_loop_) {}
+ // Test implementation.
void SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
sync_manager_.Init(temp_dir_.path(), "bogus", 0, false,
- new TestHttpPostProviderFactory(), NULL, "bogus",
+ new TestHttpPostProviderFactory(), this, "bogus",
SyncCredentials(), notifier::NotifierOptions(),
"", true /* setup_for_test_mode */);
+ sync_manager_.SetObserver(&observer_);
+ ModelSafeRoutingInfo routes;
+ GetModelSafeRoutingInfo(&routes);
+ for (ModelSafeRoutingInfo::iterator i = routes.begin(); i != routes.end();
+ ++i) {
+ EXPECT_CALL(observer_, OnChangesApplied(i->first, _, _, 1))
+ .RetiresOnSaturation();
+ EXPECT_CALL(observer_, OnChangesComplete(i->first))
+ .RetiresOnSaturation();
+ type_roots_[i->first] = MakeServerNodeForType(
+ sync_manager_.GetUserShare(), i->first);
+ }
}
void TearDown() {
+ sync_manager_.RemoveObserver();
sync_manager_.Shutdown();
}
+ // ModelSafeWorkerRegistrar implementation.
+ virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {
+ NOTIMPLEMENTED();
+ out->clear();
+ }
+ virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
+ (*out)[syncable::NIGORI] = browser_sync::GROUP_PASSIVE;
+ (*out)[syncable::BOOKMARKS] = browser_sync::GROUP_PASSIVE;
+ (*out)[syncable::THEMES] = browser_sync::GROUP_PASSIVE;
+ (*out)[syncable::SESSIONS] = browser_sync::GROUP_PASSIVE;
+ }
+
+ // Helper methods.
+ bool SetUpEncryption() {
+ // We need to create the nigori node as if it were an applied server update.
+ UserShare* share = sync_manager_.GetUserShare();
+ int64 nigori_id = GetIdForDataType(syncable::NIGORI);
+ if (nigori_id == kInvalidId)
+ return false;
+
+ // Set the nigori cryptographer information.
+ Cryptographer* cryptographer = share->dir_manager->cryptographer();
+ if (!cryptographer)
+ return false;
+ KeyParams params = {"localhost", "dummy", "foobar"};
+ cryptographer->AddKey(params);
+ sync_pb::NigoriSpecifics nigori;
+ cryptographer->GetKeys(nigori.mutable_encrypted());
+ WriteTransaction trans(share);
+ WriteNode node(&trans);
+ node.InitByIdLookup(nigori_id);
+ node.SetNigoriSpecifics(nigori);
+ return cryptographer->is_ready();
+ }
+
+ int64 GetIdForDataType(ModelType type) {
+ if (type_roots_.count(type) == 0)
+ return 0;
+ return type_roots_[type];
+ }
+
private:
// Needed by |ui_thread_|.
MessageLoopForUI ui_loop_;
@@ -391,9 +560,12 @@ class SyncManagerTest : public testing::Test {
BrowserThread ui_thread_;
// Needed by |sync_manager_|.
ScopedTempDir temp_dir_;
+ // Sync Id's for the roots of the enabled datatypes.
+ std::map<ModelType, int64> type_roots_;
protected:
SyncManager sync_manager_;
+ StrictMock<SyncManagerObserverMock> observer_;
};
TEST_F(SyncManagerTest, ParentJsEventRouter) {
@@ -675,6 +847,82 @@ TEST_F(SyncManagerTest, OnIncomingNotification) {
sync_manager_.TriggerOnIncomingNotificationForTest(model_types);
}
+TEST_F(SyncManagerTest, EncryptDataTypesWithNoData) {
+ EXPECT_TRUE(SetUpEncryption());
+ ModelTypeSet encrypted_types;
+ encrypted_types.insert(syncable::BOOKMARKS);
+ EXPECT_CALL(observer_, OnEncryptionComplete(encrypted_types));
+ sync_manager_.EncryptDataTypes(encrypted_types);
+ {
+ ReadTransaction trans(sync_manager_.GetUserShare());
+ EXPECT_EQ(encrypted_types,
+ GetEncryptedDataTypesFromSyncer(trans.GetWrappedTrans()));
+ }
+}
+
+TEST_F(SyncManagerTest, EncryptDataTypesWithData) {
+ size_t batch_size = 5;
+ EXPECT_TRUE(SetUpEncryption());
+
+ // Create some unencrypted unsynced data.
+ int64 folder = MakeFolderWithParent(sync_manager_.GetUserShare(),
+ syncable::BOOKMARKS,
+ GetIdForDataType(syncable::BOOKMARKS),
+ NULL);
+ // First batch_size nodes are children of folder.
+ size_t i;
+ for (i = 0; i < batch_size; ++i) {
+ MakeNodeWithParent(sync_manager_.GetUserShare(), syncable::BOOKMARKS,
+ StringPrintf("%zu", i), folder);
+ }
+ // Next batch_size nodes are a different type and on their own.
+ for (; i < 2*batch_size; ++i) {
+ MakeNodeWithParent(sync_manager_.GetUserShare(), syncable::SESSIONS,
+ StringPrintf("%zu", i),
+ GetIdForDataType(syncable::SESSIONS));
+ }
+ // Last batch_size nodes are a third type that will not need encryption.
+ for (; i < 3*batch_size; ++i) {
+ MakeNodeWithParent(sync_manager_.GetUserShare(), syncable::THEMES,
+ StringPrintf("%zu", i),
+ GetIdForDataType(syncable::THEMES));
+ }
+
+ {
+ ReadTransaction trans(sync_manager_.GetUserShare());
+ EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
+ syncable::BOOKMARKS,
+ false /* not encrypted */));
+ EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
+ syncable::SESSIONS,
+ false /* not encrypted */));
+ EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
+ syncable::THEMES,
+ false /* not encrypted */));
+ }
+
+ ModelTypeSet encrypted_types;
+ encrypted_types.insert(syncable::BOOKMARKS);
+ encrypted_types.insert(syncable::SESSIONS);
+ EXPECT_CALL(observer_, OnEncryptionComplete(encrypted_types));
+ sync_manager_.EncryptDataTypes(encrypted_types);
+
+ {
+ ReadTransaction trans(sync_manager_.GetUserShare());
+ EXPECT_EQ(encrypted_types,
+ GetEncryptedDataTypesFromSyncer(trans.GetWrappedTrans()));
+ EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
+ syncable::BOOKMARKS,
+ true /* is encrypted */));
+ EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
+ syncable::SESSIONS,
+ true /* is encrypted */));
+ EXPECT_TRUE(syncable::VerifyDataTypeEncryption(trans.GetWrappedTrans(),
+ syncable::THEMES,
+ false /* not encrypted */));
+ }
+}
+
} // namespace
} // namespace browser_sync

Powered by Google App Engine
This is Rietveld 408576698