diff options
author | skrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-14 20:31:25 +0000 |
---|---|---|
committer | skrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-14 20:31:25 +0000 |
commit | 76353a19f68031f9364c647f74c02deb957cdeed (patch) | |
tree | f0cc06c605e23e403fd33615d7003188f5284ae2 | |
parent | 923f3e2a704f61e3155c4e5555197962d50b854e (diff) | |
download | chromium_src-76353a19f68031f9364c647f74c02deb957cdeed.zip chromium_src-76353a19f68031f9364c647f74c02deb957cdeed.tar.gz chromium_src-76353a19f68031f9364c647f74c02deb957cdeed.tar.bz2 |
Revert "Revert 48932 - Merge certain preferences during model association."
This reverts commit 392d1efa36fba717950cbfec40055939493a27b2.
Review URL: http://codereview.chromium.org/2676007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49720 0039d316-1c4b-4281-b951-d872f2087c98
9 files changed, 1003 insertions, 286 deletions
diff --git a/chrome/browser/sync/abstract_profile_sync_service_test.h b/chrome/browser/sync/abstract_profile_sync_service_test.h new file mode 100644 index 0000000..7554528 --- /dev/null +++ b/chrome/browser/sync/abstract_profile_sync_service_test.h @@ -0,0 +1,125 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_ +#define CHROME_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_ + +#include <string> + +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/sync/engine/syncapi.h" +#include "chrome/browser/sync/glue/autofill_model_associator.h" +#include "chrome/browser/sync/glue/preference_model_associator.h" +#include "chrome/browser/sync/glue/sync_backend_host_mock.h" +#include "chrome/browser/sync/profile_sync_factory_mock.h" +#include "chrome/browser/sync/protocol/sync.pb.h" +#include "chrome/browser/sync/syncable/directory_manager.h" +#include "chrome/browser/sync/syncable/model_type.h" +#include "chrome/browser/sync/syncable/syncable.h" +#include "chrome/browser/sync/test_profile_sync_service.h" +#include "chrome/test/profile_mock.h" +#include "chrome/test/sync/engine/test_id_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +using browser_sync::SyncBackendHostMock; +using browser_sync::TestIdFactory; +using sync_api::UserShare; +using syncable::BASE_VERSION; +using syncable::CREATE; +using syncable::DirectoryManager; +using syncable::ID; +using syncable::IS_DEL; +using syncable::IS_DIR; +using syncable::IS_UNAPPLIED_UPDATE; +using syncable::IS_UNSYNCED; +using syncable::ModelType; +using syncable::MutableEntry; +using syncable::SERVER_IS_DIR; +using syncable::SERVER_VERSION; +using syncable::SPECIFICS; +using syncable::ScopedDirLookup; +using syncable::UNIQUE_SERVER_TAG; +using syncable::UNITTEST; +using syncable::WriteTransaction; + +class AbstractProfileSyncServiceTest : public testing::Test { + protected: + AbstractProfileSyncServiceTest() + : ui_thread_(ChromeThread::UI, &message_loop_) {} + + bool CreateRoot(ModelType model_type) { + UserShare* user_share = service_->backend()->GetUserShareHandle(); + DirectoryManager* dir_manager = user_share->dir_manager.get(); + + ScopedDirLookup dir(dir_manager, user_share->authenticated_name); + if (!dir.good()) + return false; + + std::string tag_name; + switch (model_type) { + case syncable::AUTOFILL: + tag_name = browser_sync::kAutofillTag; + break; + case syncable::PREFERENCES: + tag_name = browser_sync::kPreferencesTag; + break; + default: + return false; + } + + WriteTransaction wtrans(dir, UNITTEST, __FILE__, __LINE__); + MutableEntry node(&wtrans, + CREATE, + wtrans.root_id(), + tag_name); + node.Put(UNIQUE_SERVER_TAG, tag_name); + node.Put(IS_DIR, true); + node.Put(SERVER_IS_DIR, false); + node.Put(IS_UNSYNCED, false); + node.Put(IS_UNAPPLIED_UPDATE, false); + node.Put(SERVER_VERSION, 20); + node.Put(BASE_VERSION, 20); + node.Put(IS_DEL, false); + node.Put(ID, ids_.MakeServer(tag_name)); + sync_pb::EntitySpecifics specifics; + syncable::AddDefaultExtensionValue(model_type, &specifics); + node.Put(SPECIFICS, specifics); + + return true; + } + + friend class CreateRootTask; + + MessageLoopForUI message_loop_; + ChromeThread ui_thread_; + ProfileSyncFactoryMock factory_; + ProfileSyncServiceObserverMock observer_; + SyncBackendHostMock backend_; + scoped_ptr<TestProfileSyncService> service_; + TestIdFactory ids_; +}; + +class CreateRootTask : public Task { + public: + explicit CreateRootTask(AbstractProfileSyncServiceTest* test, + ModelType model_type) + : test_(test), model_type_(model_type), success_(false) { + } + + virtual void Run() { + success_ = test_->CreateRoot(model_type_); + } + + bool success() { return success_; } + + private: + AbstractProfileSyncServiceTest* test_; + ModelType model_type_; + bool success_; +}; + +#endif // CHROME_BROWSER_SYNC_ABSTRACT_PROFILE_SYNC_SERVICE_TEST_H_ diff --git a/chrome/browser/sync/glue/preference_change_processor.cc b/chrome/browser/sync/glue/preference_change_processor.cc index 8e11f9d..a379052 100644 --- a/chrome/browser/sync/glue/preference_change_processor.cc +++ b/chrome/browser/sync/glue/preference_change_processor.cc @@ -48,7 +48,7 @@ void PreferenceChangeProcessor::Observe(NotificationType type, pref_service_->FindPreference((*name).c_str()); DCHECK(preference); - // TODO (mnissler) Detect preference->IsManaged() state changes here and call + // TODO(mnissler): Detect preference->IsManaged() state changes here and call // into PreferenceModelAssociator to associate/disassociate sync nodes when // the state changes. @@ -59,11 +59,26 @@ void PreferenceChangeProcessor::Observe(NotificationType type, sync_api::WriteTransaction trans(share_handle()); sync_api::WriteNode node(&trans); + // Since we don't create sync nodes for preferences that still have + // their default values, this changed preference may not have a sync + // node yet. If not, create it. int64 sync_id = model_associator_->GetSyncIdFromChromeId(*name); if (sync_api::kInvalidId == sync_id) { - std::wstring err = L"Unexpected notification for: " + *name; - error_handler()->OnUnrecoverableError(FROM_HERE, WideToUTF8(err)); - return; + sync_api::ReadNode root(&trans); + if (!root.InitByTagLookup(browser_sync::kPreferencesTag)) { + error_handler()->OnUnrecoverableError(FROM_HERE, "Can't find root."); + return; + } + + std::string tag = WideToUTF8(*name); + if (!node.InitUniqueByCreation(syncable::PREFERENCES, root, tag)) { + error_handler()->OnUnrecoverableError( + FROM_HERE, + "Failed to create preference sync node."); + return; + } + + model_associator_->Associate(preference, node.GetId()); } else { if (!node.InitByIdLookup(sync_id)) { error_handler()->OnUnrecoverableError(FROM_HERE, @@ -72,7 +87,7 @@ void PreferenceChangeProcessor::Observe(NotificationType type, } } - if (!WritePreference(&node, *name, preference->GetValue())) { + if (!PreferenceModelAssociator::WritePreferenceToNode(*preference, &node)) { error_handler()->OnUnrecoverableError(FROM_HERE, "Failed to update preference node."); return; @@ -135,6 +150,15 @@ void PreferenceChangeProcessor::ApplyChangesFromSyncModel( pref_service_->ClearPref(pref_name); } else { pref_service_->Set(pref_name, *value); + + // If this is a newly added node, associate. + if (sync_api::SyncManager::ChangeRecord::ACTION_ADD == + changes[i].action) { + const PrefService::Preference* preference = + pref_service_->FindPreference(name.c_str()); + model_associator_->Associate(preference, changes[i].id); + } + if (0 == name.compare(prefs::kShowBookmarkBar)) { // If it was the bookmark bar, send an additional notification. NotificationService::current()->Notify( @@ -147,26 +171,6 @@ void PreferenceChangeProcessor::ApplyChangesFromSyncModel( StartObserving(); } -bool PreferenceChangeProcessor::WritePreference( - sync_api::WriteNode* node, - const std::wstring& name, - const Value* value) { - std::string serialized; - JSONStringValueSerializer json(&serialized); - if (!json.Serialize(*value)) { - error_handler()->OnUnrecoverableError(FROM_HERE, - "Failed to serialize preference value."); - return false; - } - - sync_pb::PreferenceSpecifics preference; - preference.set_name(WideToUTF8(name)); - preference.set_value(serialized); - node->SetPreferenceSpecifics(preference); - node->SetTitle(name); - return true; -} - Value* PreferenceChangeProcessor::ReadPreference( sync_api::ReadNode* node, std::wstring* name) { diff --git a/chrome/browser/sync/glue/preference_change_processor.h b/chrome/browser/sync/glue/preference_change_processor.h index 10f16bcb..b604c96 100644 --- a/chrome/browser/sync/glue/preference_change_processor.h +++ b/chrome/browser/sync/glue/preference_change_processor.h @@ -46,9 +46,6 @@ class PreferenceChangeProcessor : public ChangeProcessor, virtual void StopImpl(); private: - bool WritePreference(sync_api::WriteNode* node, - const std::wstring& name, - const Value* value); Value* ReadPreference(sync_api::ReadNode* node, std::wstring* name); void StartObserving(); diff --git a/chrome/browser/sync/glue/preference_model_associator.cc b/chrome/browser/sync/glue/preference_model_associator.cc index 03794a3..2314824 100644 --- a/chrome/browser/sync/glue/preference_model_associator.cc +++ b/chrome/browser/sync/glue/preference_model_associator.cc @@ -16,6 +16,7 @@ #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/protocol/preference_specifics.pb.h" #include "chrome/common/json_value_serializer.h" +#include "chrome/common/pref_names.h" namespace browser_sync { @@ -69,7 +70,7 @@ bool PreferenceModelAssociator::AssociateModels() { pref_service->FindPreference((*it).c_str()); DCHECK(pref); - sync_api::ReadNode node(&trans); + sync_api::WriteNode node(&trans); if (node.InitByClientTagLookup(syncable::PREFERENCES, tag)) { const sync_pb::PreferenceSpecifics& preference( node.GetPreferenceSpecifics()); @@ -85,32 +86,36 @@ bool PreferenceModelAssociator::AssociateModels() { return false; } - // Update the local preference based on what we got from the sync - // server. + // Merge the server value of this preference with the local value. + bool node_needs_update = MergePreference(*pref, value.get()); + + // Update the local preference based on what we got from the + // sync server. pref_service->Set(pref_name.c_str(), *value); + + // If the merge resulted in an updated value, write it back to + // the sync node. + if (node_needs_update && !WritePreferenceToNode(*pref, &node)) + return false; } Associate(pref, node.GetId()); } else if (!pref->IsManaged()) { - sync_api::WriteNode node(&trans); - if (!node.InitUniqueByCreation(syncable::PREFERENCES, root, tag)) { + // If there is no server value for this preference and it is + // currently its default value, don't create a new server node. + if (pref->IsDefaultValue()) + continue; + + sync_api::WriteNode write_node(&trans); + if (!write_node.InitUniqueByCreation(syncable::PREFERENCES, root, tag)) { LOG(ERROR) << "Failed to create preference sync node."; return false; } // Update the sync node with the local value for this preference. - std::string serialized; - JSONStringValueSerializer json(&serialized); - if (!json.Serialize(*(pref->GetValue()))) { - LOG(ERROR) << "Failed to serialize preference value."; + if (!WritePreferenceToNode(*pref, &write_node)) return false; - } - sync_pb::PreferenceSpecifics preference; - preference.set_name(tag); - preference.set_value(serialized); - node.SetPreferenceSpecifics(preference); - node.SetTitle(*it); - Associate(pref, node.GetId()); + Associate(pref, write_node.GetId()); } } return true; @@ -192,4 +197,95 @@ bool PreferenceModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, return true; } +bool PreferenceModelAssociator::MergePreference( + const PrefService::Preference& local_pref, + Value* server_value) { + + if (local_pref.name() == prefs::kURLsToRestoreOnStartup || + local_pref.name() == prefs::kDesktopNotificationAllowedOrigins || + local_pref.name() == prefs::kDesktopNotificationDeniedOrigins) { + return MergeListValues(*local_pref.GetValue(), server_value); + } + + if (local_pref.name() == prefs::kContentSettingsPatterns || + local_pref.name() == prefs::kGeolocationContentSettings) { + return MergeDictionaryValues(*local_pref.GetValue(), server_value); + } + return false; +} + +bool PreferenceModelAssociator::WritePreferenceToNode( + const PrefService::Preference& pref, + sync_api::WriteNode* node) { + std::string serialized; + JSONStringValueSerializer json(&serialized); + if (!json.Serialize(*pref.GetValue())) { + LOG(ERROR) << "Failed to serialize preference value."; + return false; + } + + sync_pb::PreferenceSpecifics preference; + preference.set_name(WideToUTF8(pref.name())); + preference.set_value(serialized); + node->SetPreferenceSpecifics(preference); + node->SetTitle(pref.name()); + return true; +} + +bool PreferenceModelAssociator::MergeListValues(const Value& from_value, + Value* to_value) { + if (from_value.GetType() == Value::TYPE_NULL) + return false; + bool to_changed = false; + + DCHECK(from_value.GetType() == Value::TYPE_LIST); + DCHECK(to_value->GetType() == Value::TYPE_LIST); + const ListValue& from_list_value = static_cast<const ListValue&>(from_value); + ListValue* to_list_value = static_cast<ListValue*>(to_value); + + for (ListValue::const_iterator i = from_list_value.begin(); + i != from_list_value.end(); ++i) { + Value* value = (*i)->DeepCopy(); + if (to_list_value->AppendIfNotPresent(value)) { + to_changed = true; + } else { + delete value; + } + } + return to_changed; +} + +bool PreferenceModelAssociator::MergeDictionaryValues(const Value& from_value, + Value* to_value) { + if (from_value.GetType() == Value::TYPE_NULL) + return false; + bool to_changed = false; + + DCHECK(from_value.GetType() == Value::TYPE_DICTIONARY); + DCHECK(to_value->GetType() == Value::TYPE_DICTIONARY); + const DictionaryValue& from_dict_value = + static_cast<const DictionaryValue&>(from_value); + DictionaryValue* to_dict_value = static_cast<DictionaryValue*>(to_value); + + for (DictionaryValue::key_iterator key = from_dict_value.begin_keys(); + key != from_dict_value.end_keys(); ++key) { + Value* from_value; + bool success = from_dict_value.GetWithoutPathExpansion(*key, &from_value); + DCHECK(success); + + Value* to_value; + if (to_dict_value->GetWithoutPathExpansion(*key, &to_value)) { + if (to_value->GetType() == Value::TYPE_DICTIONARY) { + to_changed = MergeDictionaryValues(*from_value, to_value); + } + // Note that for all other types we want to preserve the "to" + // values so we do nothing here. + } else { + to_dict_value->SetWithoutPathExpansion(*key, from_value->DeepCopy()); + to_changed = true; + } + } + return to_changed; +} + } // namespace browser_sync diff --git a/chrome/browser/sync/glue/preference_model_associator.h b/chrome/browser/sync/glue/preference_model_associator.h index 89f2fb9..cbf890e 100644 --- a/chrome/browser/sync/glue/preference_model_associator.h +++ b/chrome/browser/sync/glue/preference_model_associator.h @@ -16,6 +16,11 @@ #include "chrome/browser/sync/unrecoverable_error_handler.h" class ProfileSyncService; +class Value; + +namespace sync_api { +class WriteNode; +} namespace browser_sync { @@ -86,6 +91,20 @@ class PreferenceModelAssociator // |sync_id| with that node's id. virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id); + // Merges the value of local_pref into the supplies server_value. + // If there is a conflict, the server value always takes precedence. + // The method returns true if server_value was modified. Note that + // only certain preferences are allowed to be merged, see the + // method's implementation for details. + static bool MergePreference(const PrefService::Preference& local_pref, + Value* server_value); + + // Writes the value of pref into the specified node. Returns true + // upon success. + static bool WritePreferenceToNode(const PrefService::Preference& pref, + sync_api::WriteNode* node); + + protected: // Returns sync service instance. ProfileSyncService* sync_service() { return sync_service_; } @@ -93,6 +112,9 @@ class PreferenceModelAssociator typedef std::map<std::wstring, int64> PreferenceNameToSyncIdMap; typedef std::map<int64, std::wstring> SyncIdToPreferenceNameMap; + static bool MergeListValues(const Value& from_value, Value* to_value); + static bool MergeDictionaryValues(const Value& from_value, Value* to_value); + ProfileSyncService* sync_service_; std::set<std::wstring> synced_preferences_; int64 preferences_node_id_; diff --git a/chrome/browser/sync/glue/preference_model_associator_unittest.cc b/chrome/browser/sync/glue/preference_model_associator_unittest.cc new file mode 100644 index 0000000..e8f87f6 --- /dev/null +++ b/chrome/browser/sync/glue/preference_model_associator_unittest.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_ptr.h" +#include "base/values.h" +#include "chrome/browser/sync/glue/preference_model_associator.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/testing_profile.h" +#include "testing/gtest/include/gtest/gtest.h" + +using browser_sync::PreferenceModelAssociator; + +class AbstractPreferenceMergeTest : public testing::Test { + protected: + virtual void SetUp() { + pref_service_ = profile_.GetPrefs(); + } + + void SetContentPattern(DictionaryValue* patterns_dict, + const std::wstring& expression, + const std::wstring& content_type, + int setting) { + DictionaryValue* expression_dict; + bool found = + patterns_dict->GetDictionaryWithoutPathExpansion(expression, + &expression_dict); + if (!found) { + expression_dict = new DictionaryValue; + patterns_dict->SetWithoutPathExpansion(expression, expression_dict); + } + expression_dict->SetWithoutPathExpansion( + content_type, + Value::CreateIntegerValue(setting)); + } + + TestingProfile profile_; + PrefService* pref_service_; +}; + +class ListPreferenceMergeTest : public AbstractPreferenceMergeTest { + protected: + ListPreferenceMergeTest() : + server_url0_(L"http://example.com/server0"), + server_url1_(L"http://example.com/server1"), + local_url0_(L"http://example.com/local0"), + local_url1_(L"http://example.com/local1") {} + + virtual void SetUp() { + AbstractPreferenceMergeTest::SetUp(); + server_url_list_.Append(Value::CreateStringValue(server_url0_)); + server_url_list_.Append(Value::CreateStringValue(server_url1_)); + } + + std::wstring server_url0_; + std::wstring server_url1_; + std::wstring local_url0_; + std::wstring local_url1_; + ListValue server_url_list_; +}; + +TEST_F(ListPreferenceMergeTest, LocalNull) { + const PrefService::Preference* pref = + pref_service_->FindPreference(prefs::kURLsToRestoreOnStartup); + scoped_ptr<Value> original(server_url_list_.DeepCopy()); + EXPECT_FALSE(PreferenceModelAssociator::MergePreference(*pref, + &server_url_list_)); + EXPECT_TRUE(server_url_list_.Equals(original.get())); +} + +TEST_F(ListPreferenceMergeTest, Merge) { + ListValue* local_list_value = + pref_service_->GetMutableList(prefs::kURLsToRestoreOnStartup); + local_list_value->Append(Value::CreateStringValue(local_url0_)); + local_list_value->Append(Value::CreateStringValue(local_url1_)); + + const PrefService::Preference* pref = + pref_service_->FindPreference(prefs::kURLsToRestoreOnStartup); + EXPECT_TRUE(PreferenceModelAssociator::MergePreference(*pref, + &server_url_list_)); + + ListValue expected; + expected.Append(Value::CreateStringValue(server_url0_)); + expected.Append(Value::CreateStringValue(server_url1_)); + expected.Append(Value::CreateStringValue(local_url0_)); + expected.Append(Value::CreateStringValue(local_url1_)); + EXPECT_TRUE(server_url_list_.Equals(&expected)); +} + +TEST_F(ListPreferenceMergeTest, Duplicates) { + ListValue* local_list_value = + pref_service_->GetMutableList(prefs::kURLsToRestoreOnStartup); + local_list_value->Append(Value::CreateStringValue(local_url0_)); + local_list_value->Append(Value::CreateStringValue(server_url0_)); + local_list_value->Append(Value::CreateStringValue(server_url1_)); + + const PrefService::Preference* pref = + pref_service_->FindPreference(prefs::kURLsToRestoreOnStartup); + EXPECT_TRUE(PreferenceModelAssociator::MergePreference(*pref, + &server_url_list_)); + + ListValue expected; + expected.Append(Value::CreateStringValue(server_url0_)); + expected.Append(Value::CreateStringValue(server_url1_)); + expected.Append(Value::CreateStringValue(local_url0_)); + EXPECT_TRUE(server_url_list_.Equals(&expected)); +} + +TEST_F(ListPreferenceMergeTest, Equals) { + ListValue* local_list_value = + pref_service_->GetMutableList(prefs::kURLsToRestoreOnStartup); + local_list_value->Append(Value::CreateStringValue(server_url0_)); + local_list_value->Append(Value::CreateStringValue(server_url1_)); + + scoped_ptr<Value> original(server_url_list_.DeepCopy()); + const PrefService::Preference* pref = + pref_service_->FindPreference(prefs::kURLsToRestoreOnStartup); + EXPECT_FALSE(PreferenceModelAssociator::MergePreference(*pref, + &server_url_list_)); + EXPECT_TRUE(server_url_list_.Equals(original.get())); +} + +class DictionaryPreferenceMergeTest : public AbstractPreferenceMergeTest { + protected: + DictionaryPreferenceMergeTest() : + expression0_(L"expression0"), + expression1_(L"expression1"), + expression2_(L"expression2"), + content_type0_(L"content_type0"), + content_type1_(L"content_type1") {} + + virtual void SetUp() { + AbstractPreferenceMergeTest::SetUp(); + SetContentPattern(&server_patterns_, expression0_, content_type0_, 1); + SetContentPattern(&server_patterns_, expression0_, content_type1_, 2); + SetContentPattern(&server_patterns_, expression1_, content_type0_, 1); + } + + std::wstring expression0_; + std::wstring expression1_; + std::wstring expression2_; + std::wstring content_type0_; + std::wstring content_type1_; + DictionaryValue server_patterns_; +}; + +TEST_F(DictionaryPreferenceMergeTest, LocalNull) { + scoped_ptr<Value> original(server_patterns_.DeepCopy()); + EXPECT_FALSE(PreferenceModelAssociator::MergePreference( + *pref_service_->FindPreference(prefs::kContentSettingsPatterns), + &server_patterns_)); + EXPECT_TRUE(server_patterns_.Equals(original.get())); +} + +TEST_F(DictionaryPreferenceMergeTest, MergeNoConflicts) { + DictionaryValue* local_dict_value = + pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns); + SetContentPattern(local_dict_value, expression2_, content_type0_, 1); + + EXPECT_TRUE(PreferenceModelAssociator::MergePreference( + *pref_service_->FindPreference(prefs::kContentSettingsPatterns), + &server_patterns_)); + + DictionaryValue expected; + SetContentPattern(&expected, expression0_, content_type0_, 1); + SetContentPattern(&expected, expression0_, content_type1_, 2); + SetContentPattern(&expected, expression1_, content_type0_, 1); + SetContentPattern(&expected, expression2_, content_type0_, 1); + EXPECT_TRUE(server_patterns_.Equals(&expected)); +} + +TEST_F(DictionaryPreferenceMergeTest, MergeConflicts) { + DictionaryValue* local_dict_value = + pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns); + SetContentPattern(local_dict_value, expression0_, content_type0_, 2); + SetContentPattern(local_dict_value, expression1_, content_type0_, 1); + SetContentPattern(local_dict_value, expression1_, content_type1_, 1); + SetContentPattern(local_dict_value, expression2_, content_type0_, 2); + + EXPECT_TRUE(PreferenceModelAssociator::MergePreference( + *pref_service_->FindPreference(prefs::kContentSettingsPatterns), + &server_patterns_)); + + DictionaryValue expected; + SetContentPattern(&expected, expression0_, content_type0_, 1); + SetContentPattern(&expected, expression0_, content_type1_, 2); + SetContentPattern(&expected, expression1_, content_type0_, 1); + SetContentPattern(&expected, expression1_, content_type1_, 1); + SetContentPattern(&expected, expression2_, content_type0_, 2); + EXPECT_TRUE(server_patterns_.Equals(&expected)); +} + +TEST_F(DictionaryPreferenceMergeTest, Equal) { + DictionaryValue* local_dict_value = + pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns); + SetContentPattern(local_dict_value, expression0_, content_type0_, 1); + SetContentPattern(local_dict_value, expression0_, content_type1_, 2); + SetContentPattern(local_dict_value, expression1_, content_type0_, 1); + + scoped_ptr<Value> original(server_patterns_.DeepCopy()); + EXPECT_FALSE(PreferenceModelAssociator::MergePreference( + *pref_service_->FindPreference(prefs::kContentSettingsPatterns), + &server_patterns_)); + EXPECT_TRUE(server_patterns_.Equals(original.get())); +} + +TEST_F(DictionaryPreferenceMergeTest, ConflictButServerWins) { + DictionaryValue* local_dict_value = + pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns); + SetContentPattern(local_dict_value, expression0_, content_type0_, 2); + SetContentPattern(local_dict_value, expression0_, content_type1_, 2); + SetContentPattern(local_dict_value, expression1_, content_type0_, 1); + + scoped_ptr<Value> original(server_patterns_.DeepCopy()); + EXPECT_FALSE(PreferenceModelAssociator::MergePreference( + *pref_service_->FindPreference(prefs::kContentSettingsPatterns), + &server_patterns_)); + EXPECT_TRUE(server_patterns_.Equals(original.get())); +} + +class IndividualPreferenceMergeTest : public AbstractPreferenceMergeTest { + protected: + IndividualPreferenceMergeTest() : + url0_(L"http://example.com/server0"), + url1_(L"http://example.com/server1"), + expression0_(L"expression0"), + expression1_(L"expression1"), + content_type0_(L"content_type0") {} + + virtual void SetUp() { + AbstractPreferenceMergeTest::SetUp(); + server_url_list_.Append(Value::CreateStringValue(url0_)); + SetContentPattern(&server_patterns_, expression0_, content_type0_, 1); + } + + bool MergeListPreference(const wchar_t* pref) { + ListValue* local_list_value = pref_service_->GetMutableList(pref); + local_list_value->Append(Value::CreateStringValue(url1_)); + + return PreferenceModelAssociator::MergePreference( + *pref_service_->FindPreference(pref), + &server_url_list_); + } + + bool MergeDictionaryPreference(const wchar_t* pref) { + DictionaryValue* local_dict_value = + pref_service_->GetMutableDictionary(pref); + SetContentPattern(local_dict_value, expression1_, content_type0_, 1); + + return PreferenceModelAssociator::MergePreference( + *pref_service_->FindPreference(pref), + &server_patterns_); + } + + std::wstring url0_; + std::wstring url1_; + std::wstring expression0_; + std::wstring expression1_; + std::wstring content_type0_; + ListValue server_url_list_; + DictionaryValue server_patterns_; +}; + +TEST_F(IndividualPreferenceMergeTest, URLsToRestoreOnStartup) { + EXPECT_TRUE(MergeListPreference(prefs::kURLsToRestoreOnStartup)); +} + +TEST_F(IndividualPreferenceMergeTest, DesktopNotificationAllowedOrigins) { + EXPECT_TRUE(MergeListPreference(prefs::kDesktopNotificationAllowedOrigins)); +} + +TEST_F(IndividualPreferenceMergeTest, DesktopNotificationDeniedOrigins) { + EXPECT_TRUE(MergeListPreference(prefs::kDesktopNotificationDeniedOrigins)); +} + +TEST_F(IndividualPreferenceMergeTest, ContentSettingsPatterns) { + EXPECT_TRUE(MergeDictionaryPreference(prefs::kContentSettingsPatterns)); +} + +TEST_F(IndividualPreferenceMergeTest, GeolocationContentSettings) { + EXPECT_TRUE(MergeDictionaryPreference(prefs::kGeolocationContentSettings)); +} diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc index 4ca8e6e..21a7b07 100644 --- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc @@ -8,32 +8,31 @@ #include "testing/gtest/include/gtest/gtest.h" +#include "base/message_loop.h" #include "base/ref_counted.h" +#include "base/scoped_ptr.h" #include "base/string16.h" #include "base/task.h" #include "base/time.h" #include "base/utf_string_conversions.h" #include "base/waitable_event.h" #include "chrome/browser/autofill/autofill_common_unittest.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/sync/abstract_profile_sync_service_test.h" #include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/glue/autofill_change_processor.h" #include "chrome/browser/sync/glue/autofill_data_type_controller.h" #include "chrome/browser/sync/glue/autofill_model_associator.h" -#include "chrome/browser/sync/glue/sync_backend_host_mock.h" #include "chrome/browser/sync/profile_sync_factory.h" -#include "chrome/browser/sync/profile_sync_factory_mock.h" #include "chrome/browser/sync/profile_sync_service.h" -#include "chrome/browser/sync/profile_sync_test_util.h" -#include "chrome/browser/sync/test_profile_sync_service.h" #include "chrome/browser/sync/protocol/autofill_specifics.pb.h" -#include "chrome/browser/sync/syncable/directory_manager.h" +#include "chrome/browser/sync/syncable/model_type.h" #include "chrome/browser/sync/test_profile_sync_service.h" #include "chrome/browser/webdata/autofill_change.h" #include "chrome/browser/webdata/autofill_entry.h" #include "chrome/browser/webdata/web_database.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" -#include "chrome/test/sync/engine/test_id_factory.h" #include "chrome/test/profile_mock.h" #include "testing/gmock/include/gmock/gmock.h" @@ -42,26 +41,6 @@ using base::WaitableEvent; using browser_sync::AutofillChangeProcessor; using browser_sync::AutofillDataTypeController; using browser_sync::AutofillModelAssociator; -using browser_sync::SyncBackendHostMock; -using browser_sync::TestIdFactory; -using browser_sync::UnrecoverableErrorHandler; -using sync_api::SyncManager; -using sync_api::UserShare; -using syncable::BASE_VERSION; -using syncable::CREATE; -using syncable::DirectoryManager; -using syncable::ID; -using syncable::IS_DEL; -using syncable::IS_DIR; -using syncable::IS_UNAPPLIED_UPDATE; -using syncable::IS_UNSYNCED; -using syncable::MutableEntry; -using syncable::SERVER_IS_DIR; -using syncable::SERVER_VERSION; -using syncable::SPECIFICS; -using syncable::ScopedDirLookup; -using syncable::UNIQUE_SERVER_TAG; -using syncable::UNITTEST; using syncable::WriteTransaction; using testing::_; using testing::DoAll; @@ -120,7 +99,9 @@ class PersonalDataManagerMock: public PersonalDataManager { }; ACTION_P4(MakeAutofillSyncComponents, service, wd, pdm, dtc) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB)); + EXPECT_TRUE(ChromeThread::CurrentlyOn(ChromeThread::DB)); + if (!ChromeThread::CurrentlyOn(ChromeThread::DB)) + return ProfileSyncFactory::SyncComponents(NULL, NULL); AutofillModelAssociator* model_associator = new AutofillModelAssociator(service, wd, pdm); AutofillChangeProcessor* change_processor = @@ -129,16 +110,16 @@ ACTION_P4(MakeAutofillSyncComponents, service, wd, pdm, dtc) { change_processor); } -class ProfileSyncServiceAutofillTest : public testing::Test { +class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest { protected: - ProfileSyncServiceAutofillTest() - : ui_thread_(ChromeThread::UI, &message_loop_), - db_thread_(ChromeThread::DB) { + ProfileSyncServiceAutofillTest() : db_thread_(ChromeThread::DB) { } virtual void SetUp() { web_data_service_ = new WebDataServiceFake(&web_database_); personal_data_manager_ = new PersonalDataManagerMock(); + EXPECT_CALL(*personal_data_manager_, LoadProfiles()).Times(1); + EXPECT_CALL(*personal_data_manager_, LoadCreditCards()).Times(1); personal_data_manager_->Init(&profile_); db_thread_.Start(); @@ -153,7 +134,7 @@ class ProfileSyncServiceAutofillTest : public testing::Test { MessageLoop::current()->RunAllPending(); } - void StartSyncService(Task* task) { + void StartSyncService(Task* task, bool will_fail_association) { if (!service_.get()) { service_.reset( new TestProfileSyncService(&factory_, &profile_, false, false)); @@ -177,9 +158,17 @@ class ProfileSyncServiceAutofillTest : public testing::Test { EXPECT_CALL(profile_, GetPersonalDataManager()). WillRepeatedly(Return(personal_data_manager_.get())); - EXPECT_CALL(*(personal_data_manager_.get()), IsDataLoaded()). + EXPECT_CALL(*personal_data_manager_, IsDataLoaded()). WillRepeatedly(Return(true)); + EXPECT_CALL(backend_, RequestPause()).Times(1); + EXPECT_CALL(backend_, ConfigureDataTypes(_, _)).Times(1); + if (will_fail_association) { + EXPECT_CALL(backend_, RequestResume()).Times(0); + } else { + EXPECT_CALL(backend_, RequestResume()).Times(1); + } + // State changes once for the backend init and once for startup done. EXPECT_CALL(observer_, OnStateChanged()). WillOnce(InvokeTask(task)). @@ -191,74 +180,53 @@ class ProfileSyncServiceAutofillTest : public testing::Test { } } - void CreateAutofillRoot() { - UserShare* user_share = service_->backend()->GetUserShareHandle(); - DirectoryManager* dir_manager = user_share->dir_manager.get(); - - ScopedDirLookup dir(dir_manager, user_share->authenticated_name); - ASSERT_TRUE(dir.good()); - - WriteTransaction wtrans(dir, UNITTEST, __FILE__, __LINE__); - MutableEntry node(&wtrans, - CREATE, - wtrans.root_id(), - browser_sync::kAutofillTag); - node.Put(UNIQUE_SERVER_TAG, browser_sync::kAutofillTag); - node.Put(IS_DIR, true); - node.Put(SERVER_IS_DIR, false); - node.Put(IS_UNSYNCED, false); - node.Put(IS_UNAPPLIED_UPDATE, false); - node.Put(SERVER_VERSION, 20); - node.Put(BASE_VERSION, 20); - node.Put(IS_DEL, false); - node.Put(ID, ids_.MakeServer(browser_sync::kAutofillTag)); - sync_pb::EntitySpecifics specifics; - specifics.MutableExtension(sync_pb::autofill); - node.Put(SPECIFICS, specifics); - } - - void AddAutofillSyncNode(const AutofillEntry& entry) { + bool AddAutofillSyncNode(const AutofillEntry& entry) { sync_api::WriteTransaction trans( service_->backend()->GetUserShareHandle()); sync_api::ReadNode autofill_root(&trans); - ASSERT_TRUE(autofill_root.InitByTagLookup(browser_sync::kAutofillTag)); + if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag)) + return false; sync_api::WriteNode node(&trans); std::string tag = AutofillModelAssociator::KeyToTag(entry.key().name(), entry.key().value()); - ASSERT_TRUE(node.InitUniqueByCreation(syncable::AUTOFILL, - autofill_root, - tag)); + if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)) + return false; + AutofillChangeProcessor::WriteAutofillEntry(entry, &node); + return true; } - void AddAutofillProfileSyncNode(const AutoFillProfile& profile) { + bool AddAutofillProfileSyncNode(const AutoFillProfile& profile) { sync_api::WriteTransaction trans( service_->backend()->GetUserShareHandle()); sync_api::ReadNode autofill_root(&trans); - ASSERT_TRUE(autofill_root.InitByTagLookup(browser_sync::kAutofillTag)); + if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag)) + return false; sync_api::WriteNode node(&trans); std::string tag = AutofillModelAssociator::ProfileLabelToTag( profile.Label()); - ASSERT_TRUE(node.InitUniqueByCreation(syncable::AUTOFILL, - autofill_root, - tag)); + if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)) + return false; AutofillChangeProcessor::WriteAutofillProfile(profile, &node); sync_pb::AutofillSpecifics s(node.GetAutofillSpecifics()); s.mutable_profile()->set_label(UTF16ToUTF8(profile.Label())); node.SetAutofillSpecifics(s); + return true; } - void GetAutofillEntriesFromSyncDB(std::vector<AutofillEntry>* entries, + bool GetAutofillEntriesFromSyncDB(std::vector<AutofillEntry>* entries, std::vector<AutoFillProfile>* profiles) { sync_api::ReadTransaction trans(service_->backend()->GetUserShareHandle()); sync_api::ReadNode autofill_root(&trans); - ASSERT_TRUE(autofill_root.InitByTagLookup(browser_sync::kAutofillTag)); + if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag)) + return false; int64 child_id = autofill_root.GetFirstChildId(); while (child_id != sync_api::kInvalidId) { sync_api::ReadNode child_node(&trans); - ASSERT_TRUE(child_node.InitByIdLookup(child_id)); + if (!child_node.InitByIdLookup(child_id)) + return false; const sync_pb::AutofillSpecifics& autofill( child_node.GetAutofillSpecifics()); @@ -280,6 +248,7 @@ class ProfileSyncServiceAutofillTest : public testing::Test { } child_id = child_node.GetSuccessorId(); } + return true; } void SetIdleChangeProcessorExpectations() { @@ -307,38 +276,15 @@ class ProfileSyncServiceAutofillTest : public testing::Test { return MakeAutofillEntry(name, value, timestamp, -1); } - friend class CreateAutofillRootTask; friend class AddAutofillEntriesTask; - MessageLoopForUI message_loop_; - ChromeThread ui_thread_; ChromeThread db_thread_; scoped_refptr<ThreadNotificationService> notification_service_; - scoped_ptr<TestProfileSyncService> service_; ProfileMock profile_; - ProfileSyncFactoryMock factory_; - ProfileSyncServiceObserverMock observer_; - SyncBackendHostMock backend_; WebDatabaseMock web_database_; scoped_refptr<WebDataService> web_data_service_; scoped_refptr<PersonalDataManagerMock> personal_data_manager_; - - TestIdFactory ids_; -}; - -class CreateAutofillRootTask : public Task { - public: - explicit CreateAutofillRootTask(ProfileSyncServiceAutofillTest* test) - : test_(test) { - } - - virtual void Run() { - test_->CreateAutofillRoot(); - } - - private: - ProfileSyncServiceAutofillTest* test_; }; class AddAutofillEntriesTask : public Task { @@ -346,24 +292,30 @@ class AddAutofillEntriesTask : public Task { AddAutofillEntriesTask(ProfileSyncServiceAutofillTest* test, const std::vector<AutofillEntry>& entries, const std::vector<AutoFillProfile>& profiles) - : test_(test), entries_(entries), profiles_(profiles) { + : test_(test), entries_(entries), profiles_(profiles), success_(false) { } virtual void Run() { - test_->CreateAutofillRoot(); + if (!test_->CreateRoot(syncable::AUTOFILL)) + return; for (size_t i = 0; i < entries_.size(); ++i) { - test_->AddAutofillSyncNode(entries_[i]); + if (!test_->AddAutofillSyncNode(entries_[i])) + return; } for (size_t i = 0; i < profiles_.size(); ++i) { - test_->AddAutofillProfileSyncNode(profiles_[i]); + if (!test_->AddAutofillProfileSyncNode(profiles_[i])) + return; } - + success_ = true; } + bool success() { return success_; } + private: ProfileSyncServiceAutofillTest* test_; const std::vector<AutofillEntry>& entries_; const std::vector<AutoFillProfile>& profiles_; + bool success_; }; // TODO(skrul): Test abort startup. @@ -371,12 +323,8 @@ class AddAutofillEntriesTask : public Task { // TODO(tim): Add autofill data type controller test, and a case to cover // waiting for the PersonalDataManager. TEST_F(ProfileSyncServiceAutofillTest, FailModelAssociation) { - // Backend will be paused but not resumed. - EXPECT_CALL(backend_, RequestPause()). - WillOnce(testing::DoAll(Notify(NotificationType::SYNC_PAUSED), - testing::Return(true))); // Don't create the root autofill node so startup fails. - StartSyncService(NULL); + StartSyncService(NULL, true); EXPECT_TRUE(service_->unrecoverable_error_detected()); } @@ -384,11 +332,12 @@ TEST_F(ProfileSyncServiceAutofillTest, EmptyNativeEmptySync) { EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true)); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); std::vector<AutofillEntry> sync_entries; std::vector<AutoFillProfile> sync_profiles; - GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); EXPECT_EQ(0U, sync_entries.size()); EXPECT_EQ(0U, sync_profiles.size()); } @@ -400,11 +349,12 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) { WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); std::vector<AutofillEntry> sync_entries; std::vector<AutoFillProfile> sync_profiles; - GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); ASSERT_EQ(1U, entries.size()); EXPECT_TRUE(entries[0] == sync_entries[0]); EXPECT_EQ(0U, sync_profiles.size()); @@ -428,13 +378,14 @@ TEST_F(ProfileSyncServiceAutofillTest, HasMixedNativeEmptySync) { expected_profiles.push_back(*profile0); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)). WillOnce(DoAll(SetArgumentPointee<0>(profiles), Return(true))); - EXPECT_CALL(*(personal_data_manager_.get()), Refresh()); + EXPECT_CALL(*personal_data_manager_, Refresh()); SetIdleChangeProcessorExpectations(); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); std::vector<AutofillEntry> sync_entries; std::vector<AutoFillProfile> sync_profiles; - GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); ASSERT_EQ(1U, entries.size()); EXPECT_TRUE(entries[0] == sync_entries[0]); EXPECT_EQ(1U, sync_profiles.size()); @@ -489,7 +440,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasDuplicateProfileLabelsEmptySync) { expected_profiles.push_back(*profile1); AutoFillProfile relabelled_profile; EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true)); - EXPECT_CALL(*(personal_data_manager_.get()), Refresh()); + EXPECT_CALL(*personal_data_manager_, Refresh()); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)). WillOnce(DoAll(SetArgumentPointee<0>(profiles), Return(true))); EXPECT_CALL(web_database_, UpdateAutoFillProfile( @@ -497,11 +448,12 @@ TEST_F(ProfileSyncServiceAutofillTest, HasDuplicateProfileLabelsEmptySync) { WillOnce(DoAll(SaveArg<0>(&relabelled_profile), Return(true))); SetIdleChangeProcessorExpectations(); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); std::vector<AutofillEntry> sync_entries; std::vector<AutoFillProfile> sync_profiles; - GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); EXPECT_EQ(0U, sync_entries.size()); EXPECT_EQ(2U, sync_profiles.size()); EXPECT_EQ(expected_profiles[0], sync_profiles[1]); @@ -521,11 +473,12 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) { WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true))); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); std::vector<AutofillEntry> sync_entries; std::vector<AutoFillProfile> sync_profiles; - GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); EXPECT_EQ(2U, sync_entries.size()); } @@ -569,8 +522,9 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) { WillOnce(Return(true)); EXPECT_CALL(web_database_, AddAutoFillProfile(Eq(to_be_added))). WillOnce(Return(true)); - EXPECT_CALL(*(personal_data_manager_.get()), Refresh()); - StartSyncService(&task); + EXPECT_CALL(*personal_data_manager_, Refresh()); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); std::set<AutofillEntry> expected_entries; expected_entries.insert(native_entry); @@ -578,7 +532,8 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) { std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); std::set<AutofillEntry> new_sync_entries_set(new_sync_entries.begin(), new_sync_entries.end()); @@ -606,11 +561,13 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) { EXPECT_CALL(web_database_, UpdateAutofillEntries(ElementsAre(merged_entry))). WillOnce(Return(true)); - StartSyncService(&task); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(1U, new_sync_entries.size()); EXPECT_TRUE(merged_entry == new_sync_entries[0]); } @@ -641,12 +598,14 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) { EXPECT_CALL(web_database_, UpdateAutoFillProfile(Eq(sync_profile))). WillOnce(Return(true)); - EXPECT_CALL(*(personal_data_manager_.get()), Refresh()); - StartSyncService(&task); + EXPECT_CALL(*personal_data_manager_, Refresh()); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(1U, new_sync_profiles.size()); EXPECT_TRUE(sync_profile == new_sync_profiles[0]); } @@ -655,8 +614,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) { EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true)); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); AutofillEntry added_entry(MakeAutofillEntry("added", "entry", 1)); std::vector<base::Time> timestamps(added_entry.timestamps()); @@ -673,7 +633,8 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) { std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(1U, new_sync_entries.size()); EXPECT_TRUE(added_entry == new_sync_entries[0]); } @@ -682,8 +643,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) { EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true)); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); AutoFillProfile added_profile(string16(), 0); autofill_unittest::SetProfileInfo(&added_profile, @@ -700,7 +662,8 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) { std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(1U, new_sync_profiles.size()); EXPECT_TRUE(added_profile == new_sync_profiles[0]); } @@ -723,7 +686,8 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfileConflict) { EXPECT_CALL(web_database_, AddAutoFillProfile(Eq(sync_profile))). WillOnce(Return(true)); SetIdleChangeProcessorExpectations(); - StartSyncService(&task); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); AutoFillProfile added_profile(string16(), 0); autofill_unittest::SetProfileInfo(&added_profile, @@ -738,7 +702,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfileConflict) { EXPECT_CALL(web_database_, UpdateAutoFillProfile( ProfileMatchesExceptLabel(added_profile))). WillOnce(DoAll(SaveArg<0>(&relabelled_profile), Return(true))); - EXPECT_CALL(*(personal_data_manager_.get()), Refresh()); + EXPECT_CALL(*personal_data_manager_, Refresh()); scoped_refptr<ThreadNotifier> notifier = new ThreadNotifier(&db_thread_); notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED, @@ -747,7 +711,8 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfileConflict) { std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(2U, new_sync_profiles.size()); sync_profile.set_unique_id(0); // The sync DB doesn't store IDs. EXPECT_EQ(sync_profile, new_sync_profiles[1]); @@ -764,8 +729,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) { EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true)); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); AutofillEntry updated_entry(MakeAutofillEntry("my", "entry", 1, 2)); std::vector<base::Time> timestamps(updated_entry.timestamps()); @@ -783,7 +749,8 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) { std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(1U, new_sync_entries.size()); EXPECT_TRUE(updated_entry == new_sync_entries[0]); } @@ -800,8 +767,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfile) { EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(Return(true)); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); AutoFillProfile update_profile(string16(), 0); autofill_unittest::SetProfileInfo(&update_profile, @@ -819,7 +787,8 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfile) { std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(1U, new_sync_profiles.size()); EXPECT_TRUE(update_profile == new_sync_profiles[0]); } @@ -835,8 +804,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfileRelabel) { EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(Return(true)); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); AutoFillProfile update_profile(string16(), 0); autofill_unittest::SetProfileInfo(&update_profile, @@ -854,7 +824,8 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfileRelabel) { std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(1U, new_sync_profiles.size()); EXPECT_TRUE(update_profile == new_sync_profiles[0]); } @@ -880,15 +851,16 @@ TEST_F(ProfileSyncServiceAutofillTest, EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(Return(true)); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)). WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true))); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); josephine_update.set_label(ASCIIToUTF16("ExistingLabel")); AutoFillProfile relabelled_profile; EXPECT_CALL(web_database_, UpdateAutoFillProfile( ProfileMatchesExceptLabel(josephine_update))). WillOnce(DoAll(SaveArg<0>(&relabelled_profile), Return(true))); - EXPECT_CALL(*(personal_data_manager_.get()), Refresh()); + EXPECT_CALL(*personal_data_manager_, Refresh()); AutofillProfileChange change(AutofillProfileChange::UPDATE, josephine_update.Label(), &josephine_update, @@ -900,7 +872,8 @@ TEST_F(ProfileSyncServiceAutofillTest, std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(2U, new_sync_profiles.size()); marion.set_unique_id(0); // The sync DB doesn't store IDs. EXPECT_EQ(marion, new_sync_profiles[1]); @@ -917,8 +890,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) { EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true))); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true)); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); AutofillChangeList changes; changes.push_back(AutofillChange(AutofillChange::REMOVE, @@ -930,7 +904,8 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) { std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(0U, new_sync_entries.size()); } @@ -956,9 +931,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) { std::vector<AutoFillProfile> sync_profiles; sync_profiles.push_back(sync_profile); AddAutofillEntriesTask task(this, sync_entries, sync_profiles); - - EXPECT_CALL(*(personal_data_manager_.get()), Refresh()); - StartSyncService(&task); + EXPECT_CALL(*personal_data_manager_, Refresh()); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); AutofillProfileChange change(AutofillProfileChange::REMOVE, sync_profile.Label(), NULL, string16()); @@ -969,20 +944,22 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) { std::vector<AutofillEntry> new_sync_entries; std::vector<AutoFillProfile> new_sync_profiles; - GetAutofillEntriesFromSyncDB(&new_sync_entries, &new_sync_profiles); + ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries, + &new_sync_profiles)); ASSERT_EQ(0U, new_sync_entries.size()); } TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeError) { EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true)); EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true)); - CreateAutofillRootTask task(this); - StartSyncService(&task); + CreateRootTask task(this, syncable::AUTOFILL); + StartSyncService(&task, false); + ASSERT_TRUE(task.success()); // Inject an evil entry into the sync db to conflict with the same // entry added by the user. AutofillEntry evil_entry(MakeAutofillEntry("evil", "entry", 1)); - AddAutofillSyncNode(evil_entry); + ASSERT_TRUE(AddAutofillSyncNode(evil_entry)); AutofillChangeList changes; changes.push_back(AutofillChange(AutofillChange::ADD, diff --git a/chrome/browser/sync/profile_sync_service_preference_unittest.cc b/chrome/browser/sync/profile_sync_service_preference_unittest.cc index 79a1c6b..542875f 100644 --- a/chrome/browser/sync/profile_sync_service_preference_unittest.cc +++ b/chrome/browser/sync/profile_sync_service_preference_unittest.cc @@ -2,16 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <map> +#include <string> + #include "base/json/json_reader.h" +#include "base/stl_util-inl.h" +#include "base/task.h" +#include "chrome/browser/sync/abstract_profile_sync_service_test.h" #include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/glue/preference_change_processor.h" #include "chrome/browser/sync/glue/preference_data_type_controller.h" #include "chrome/browser/sync/glue/preference_model_associator.h" #include "chrome/browser/sync/glue/sync_backend_host.h" -#include "chrome/browser/sync/glue/sync_backend_host_mock.h" -#include "chrome/browser/sync/profile_sync_factory_mock.h" #include "chrome/browser/sync/profile_sync_test_util.h" #include "chrome/browser/sync/protocol/preference_specifics.pb.h" +#include "chrome/browser/sync/syncable/model_type.h" #include "chrome/browser/sync/test_profile_sync_service.h" #include "chrome/common/json_value_serializer.h" #include "chrome/common/pref_names.h" @@ -24,33 +29,30 @@ using browser_sync::PreferenceChangeProcessor; using browser_sync::PreferenceDataTypeController; using browser_sync::PreferenceModelAssociator; using browser_sync::SyncBackendHost; -using browser_sync::SyncBackendHostMock; using sync_api::SyncManager; using testing::_; using testing::Return; -class TestPreferenceModelAssociator : public PreferenceModelAssociator { - public: - TestPreferenceModelAssociator(ProfileSyncService* service) - : PreferenceModelAssociator(service), - helper_(new TestModelAssociatorHelper()) { - } - virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id) { - return helper_->GetSyncIdForTaggedNode(this, tag, sync_id); - } - private: - scoped_ptr<TestModelAssociatorHelper> helper_; -}; +typedef std::map<const std::wstring, const Value*> PreferenceValues; -class ProfileSyncServicePreferenceTest : public testing::Test { +class ProfileSyncServicePreferenceTest + : public AbstractProfileSyncServiceTest { protected: ProfileSyncServicePreferenceTest() - : ui_thread_(ChromeThread::UI, &message_loop_) { - } + : example_url0_(L"http://example.com/0"), + example_url1_(L"http://example.com/1"), + example_url2_(L"http://example.com/2"), + not_synced_preference_name_(L"nonsense_pref_name"), + not_synced_preference_default_value_(L"default"), + non_default_charset_value_(L"foo") {} virtual void SetUp() { profile_.reset(new TestingProfile()); profile_->set_has_history_service(true); + prefs_ = profile_->GetPrefs(); + + prefs_->RegisterStringPref(not_synced_preference_name_.c_str(), + not_synced_preference_default_value_); } virtual void TearDown() { @@ -59,51 +61,64 @@ class ProfileSyncServicePreferenceTest : public testing::Test { MessageLoop::current()->RunAllPending(); } - void StartSyncService() { - if (!service_.get()) { - service_.reset(new TestProfileSyncService(&factory_, - profile_.get(), - false, - true)); - - // Register the preference data type. - model_associator_ = new TestPreferenceModelAssociator(service_.get()); - change_processor_ = new PreferenceChangeProcessor(model_associator_, - service_.get()); - EXPECT_CALL(factory_, CreatePreferenceSyncComponents(_, _)). - WillOnce(Return(ProfileSyncFactory::SyncComponents( - model_associator_, change_processor_))); - EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). - WillOnce(MakeDataTypeManager(&backend_mock_)); - - service_->RegisterDataTypeController( - new PreferenceDataTypeController(&factory_, - service_.get())); - service_->Initialize(); + bool StartSyncService(Task* task, bool will_fail_association) { + if (service_.get()) + return false; + + service_.reset(new TestProfileSyncService( + &factory_, profile_.get(), false, false)); + service_->AddObserver(&observer_); + + // Register the preference data type. + model_associator_ = + new PreferenceModelAssociator(service_.get()); + change_processor_ = new PreferenceChangeProcessor(model_associator_, + service_.get()); + EXPECT_CALL(factory_, CreatePreferenceSyncComponents(_, _)). + WillOnce(Return(ProfileSyncFactory::SyncComponents( + model_associator_, change_processor_))); + + EXPECT_CALL(factory_, CreateDataTypeManager(_, _)). + WillOnce(MakeDataTypeManager(&backend_)); + + EXPECT_CALL(backend_, RequestPause()).Times(1); + EXPECT_CALL(backend_, ConfigureDataTypes(_, _)).Times(1); + if (will_fail_association) { + EXPECT_CALL(backend_, RequestResume()).Times(0); + } else { + EXPECT_CALL(backend_, RequestResume()).Times(1); } - // The service may have already started sync automatically if it's already - // enabled by user once. - if (!service_->HasSyncSetupCompleted()) - service_->EnableForUser(); + + EXPECT_CALL(observer_, OnStateChanged()). + WillOnce(InvokeTask(task)). + WillOnce(Return()). + WillOnce(QuitUIMessageLoop()); + service_->RegisterDataTypeController( + new PreferenceDataTypeController(&factory_, + service_.get())); + service_->Initialize(); + MessageLoop::current()->Run(); + return true; } SyncBackendHost* backend() { return service_->backend_.get(); } const Value& GetPreferenceValue(const std::wstring& name) { const PrefService::Preference* preference = - profile_->GetPrefs()->FindPreference(name.c_str()); + prefs_->FindPreference(name.c_str()); return *preference->GetValue(); } // Caller gets ownership of the returned value. const Value* GetSyncedValue(const std::wstring& name) { - sync_api::ReadTransaction trans(backend()->GetUserShareHandle()); + sync_api::ReadTransaction trans(service_->backend()->GetUserShareHandle()); sync_api::ReadNode node(&trans); int64 node_id = model_associator_->GetSyncIdFromChromeId(name); - EXPECT_NE(sync_api::kInvalidId, node_id); - EXPECT_TRUE(node.InitByIdLookup(node_id)); - EXPECT_EQ(name, node.GetTitle()); + if (node_id == sync_api::kInvalidId) + return NULL; + if (!node.InitByIdLookup(node_id)) + return NULL; const sync_pb::PreferenceSpecifics& specifics( node.GetPreferenceSpecifics()); @@ -112,15 +127,26 @@ class ProfileSyncServicePreferenceTest : public testing::Test { return reader.JsonToValue(specifics.value(), false, false); } - // Caller gets ownership of the returned change record. - SyncManager::ChangeRecord* SetSyncedValue(const std::wstring& name, - const Value& value) { + int64 SetSyncedValue(const std::wstring& name, const Value& value) { sync_api::WriteTransaction trans(backend()->GetUserShareHandle()); + sync_api::ReadNode root(&trans); + if (!root.InitByTagLookup(browser_sync::kPreferencesTag)) + return sync_api::kInvalidId; + sync_api::WriteNode node(&trans); int64 node_id = model_associator_->GetSyncIdFromChromeId(name); - EXPECT_NE(sync_api::kInvalidId, node_id); - EXPECT_TRUE(node.InitByIdLookup(node_id)); + if (node_id == sync_api::kInvalidId) { + if (!node.InitUniqueByCreation(syncable::PREFERENCES, + root, + WideToUTF8(name))) { + return sync_api::kInvalidId; + } + } else { + if (!node.InitByIdLookup(node_id)) { + return sync_api::kInvalidId; + } + } std::string serialized; JSONStringValueSerializer json(&serialized); @@ -132,60 +158,246 @@ class ProfileSyncServicePreferenceTest : public testing::Test { node.SetPreferenceSpecifics(preference); node.SetTitle(name); + return node.GetId(); + } + + SyncManager::ChangeRecord* MakeChangeRecord(const std::wstring& name, + SyncManager::ChangeRecord) { + int64 node_id = model_associator_->GetSyncIdFromChromeId(name); SyncManager::ChangeRecord* record = new SyncManager::ChangeRecord(); record->action = SyncManager::ChangeRecord::ACTION_UPDATE; record->id = node_id; return record; } - scoped_ptr<TestProfileSyncService> service_; + bool IsSynced(const std::wstring& pref_name) { + return model_associator_->synced_preferences().count(pref_name) > 0; + } + + std::string ValueString(const Value& value) { + std::string serialized; + JSONStringValueSerializer json(&serialized); + json.Serialize(value); + return serialized; + } + + friend class AddPreferenceEntriesTask; + scoped_ptr<TestingProfile> profile_; - ProfileSyncFactoryMock factory_; - SyncBackendHostMock backend_mock_; + PrefService* prefs_; PreferenceModelAssociator* model_associator_; PreferenceChangeProcessor* change_processor_; - - MessageLoopForUI message_loop_; - ChromeThread ui_thread_; + std::wstring example_url0_; + std::wstring example_url1_; + std::wstring example_url2_; + std::wstring not_synced_preference_name_; + std::wstring not_synced_preference_default_value_; + std::wstring non_default_charset_value_; }; -TEST_F(ProfileSyncServicePreferenceTest, ModelAssociation) { - StartSyncService(); - const std::set<std::wstring>& names = model_associator_->synced_preferences(); - ASSERT_LT(0U, names.size()); +class AddPreferenceEntriesTask : public Task { + public: + AddPreferenceEntriesTask(ProfileSyncServicePreferenceTest* test, + const PreferenceValues& entries) + : test_(test), entries_(entries), success_(false) { + } - for (std::set<std::wstring>::const_iterator it = names.begin(); - it != names.end(); ++it) { - const Value& expected = GetPreferenceValue(*it); - scoped_ptr<const Value> actual(GetSyncedValue(*it)); - EXPECT_TRUE(expected.Equals(actual.get())) << *it; + virtual void Run() { + if (!test_->CreateRoot(syncable::PREFERENCES)) + return; + for (PreferenceValues::const_iterator i = entries_.begin(); + i != entries_.end(); ++i) { + if (test_->SetSyncedValue(i->first, *i->second) == sync_api::kInvalidId) + return; + } + success_ = true; } + + bool success() { return success_; } + + private: + ProfileSyncServicePreferenceTest* test_; + const PreferenceValues& entries_; + bool success_; +}; + +TEST_F(ProfileSyncServicePreferenceTest, WritePreferenceToNode) { + prefs_->SetString(prefs::kHomePage, example_url0_); + CreateRootTask task(this, syncable::PREFERENCES); + ASSERT_TRUE(StartSyncService(&task, false)); + ASSERT_TRUE(task.success()); + + const PrefService::Preference* pref = + prefs_->FindPreference(prefs::kHomePage); + sync_api::WriteTransaction trans(service_->backend()->GetUserShareHandle()); + sync_api::WriteNode node(&trans); + EXPECT_TRUE(node.InitByClientTagLookup(syncable::PREFERENCES, + WideToUTF8(prefs::kHomePage))); + + EXPECT_TRUE(PreferenceModelAssociator::WritePreferenceToNode(*pref, &node)); + EXPECT_EQ(std::wstring(prefs::kHomePage), node.GetTitle()); + const sync_pb::PreferenceSpecifics& specifics(node.GetPreferenceSpecifics()); + EXPECT_EQ(WideToUTF8(prefs::kHomePage), specifics.name()); + + base::JSONReader reader; + scoped_ptr<Value> value(reader.JsonToValue(specifics.value(), false, false)); + EXPECT_TRUE(pref->GetValue()->Equals(value.get())); +} + +TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationDoNotSyncDefaults) { + const PrefService::Preference* pref = + prefs_->FindPreference(prefs::kHomePage); + EXPECT_TRUE(pref->IsDefaultValue()); + CreateRootTask task(this, syncable::PREFERENCES); + ASSERT_TRUE(StartSyncService(&task, false)); + ASSERT_TRUE(task.success()); + EXPECT_TRUE(IsSynced(prefs::kHomePage)); + EXPECT_TRUE(pref->IsDefaultValue()); + EXPECT_TRUE(GetSyncedValue(prefs::kHomePage) == NULL); + EXPECT_TRUE(GetSyncedValue(not_synced_preference_name_) == NULL); +} + +TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationEmptyCloud) { + prefs_->SetString(prefs::kHomePage, example_url0_); + ListValue* url_list = prefs_->GetMutableList(prefs::kURLsToRestoreOnStartup); + url_list->Append(Value::CreateStringValue(example_url0_)); + url_list->Append(Value::CreateStringValue(example_url1_)); + CreateRootTask task(this, syncable::PREFERENCES); + ASSERT_TRUE(StartSyncService(&task, false)); + ASSERT_TRUE(task.success()); + + scoped_ptr<const Value> value(GetSyncedValue(prefs::kHomePage)); + ASSERT_TRUE(value.get()); + EXPECT_TRUE(GetPreferenceValue(prefs::kHomePage).Equals(value.get())); + value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartup)); + ASSERT_TRUE(value.get()); + EXPECT_TRUE( + GetPreferenceValue(prefs::kURLsToRestoreOnStartup).Equals(value.get())); +} + +TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationCloudHasData) { + prefs_->SetString(prefs::kHomePage, example_url0_); + ListValue* url_list = prefs_->GetMutableList(prefs::kURLsToRestoreOnStartup); + url_list->Append(Value::CreateStringValue(example_url0_)); + url_list->Append(Value::CreateStringValue(example_url1_)); + + PreferenceValues cloud_data; + cloud_data[prefs::kHomePage] = Value::CreateStringValue(example_url1_); + ListValue* urls_to_restore = new ListValue; + urls_to_restore->Append(Value::CreateStringValue(example_url1_)); + urls_to_restore->Append(Value::CreateStringValue(example_url2_)); + cloud_data[prefs::kURLsToRestoreOnStartup] = urls_to_restore; + cloud_data[prefs::kDefaultCharset] = + Value::CreateStringValue(non_default_charset_value_); + + AddPreferenceEntriesTask task(this, cloud_data); + ASSERT_TRUE(StartSyncService(&task, false)); + ASSERT_TRUE(task.success()); + + scoped_ptr<const Value> value(GetSyncedValue(prefs::kHomePage)); + ASSERT_TRUE(value.get()); + std::wstring string_value; + EXPECT_TRUE(static_cast<const StringValue*>(value.get())-> + GetAsString(&string_value)); + EXPECT_EQ(example_url1_, string_value); + EXPECT_EQ(example_url1_, prefs_->GetString(prefs::kHomePage)); + + scoped_ptr<ListValue> expected_urls(new ListValue); + expected_urls->Append(Value::CreateStringValue(example_url1_)); + expected_urls->Append(Value::CreateStringValue(example_url2_)); + expected_urls->Append(Value::CreateStringValue(example_url0_)); + value.reset(GetSyncedValue(prefs::kURLsToRestoreOnStartup)); + ASSERT_TRUE(value.get()); + EXPECT_TRUE(value->Equals(expected_urls.get())); + EXPECT_TRUE(GetPreferenceValue(prefs::kURLsToRestoreOnStartup). + Equals(expected_urls.get())); + + value.reset(GetSyncedValue(prefs::kDefaultCharset)); + ASSERT_TRUE(value.get()); + EXPECT_TRUE(static_cast<const StringValue*>(value.get())-> + GetAsString(&string_value)); + EXPECT_EQ(non_default_charset_value_, string_value); + EXPECT_EQ(non_default_charset_value_, + prefs_->GetString(prefs::kDefaultCharset)); + STLDeleteValues(&cloud_data); +} + +TEST_F(ProfileSyncServicePreferenceTest, FailModelAssociation) { + ASSERT_TRUE(StartSyncService(NULL, true)); + EXPECT_TRUE(service_->unrecoverable_error_detected()); } -TEST_F(ProfileSyncServicePreferenceTest, UpdatedPreference) { - StartSyncService(); +TEST_F(ProfileSyncServicePreferenceTest, UpdatedPreferenceWithDefaultValue) { + const PrefService::Preference* pref = + prefs_->FindPreference(prefs::kHomePage); + EXPECT_TRUE(pref->IsDefaultValue()); - scoped_ptr<Value> expected( - Value::CreateStringValue("http://example.com/test/foo")); + CreateRootTask task(this, syncable::PREFERENCES); + ASSERT_TRUE(StartSyncService(&task, false)); + ASSERT_TRUE(task.success()); + + scoped_ptr<Value> expected(Value::CreateStringValue(example_url0_)); + profile_->GetPrefs()->Set(prefs::kHomePage, *expected); + + scoped_ptr<const Value> actual(GetSyncedValue(prefs::kHomePage)); + ASSERT_TRUE(actual.get()); + EXPECT_TRUE(expected->Equals(actual.get())); +} + +TEST_F(ProfileSyncServicePreferenceTest, UpdatedPreferenceWithValue) { + profile_->GetPrefs()->SetString(prefs::kHomePage, example_url0_); + CreateRootTask task(this, syncable::PREFERENCES); + ASSERT_TRUE(StartSyncService(&task, false)); + ASSERT_TRUE(task.success()); + + scoped_ptr<Value> expected(Value::CreateStringValue(example_url1_)); profile_->GetPrefs()->Set(prefs::kHomePage, *expected); scoped_ptr<const Value> actual(GetSyncedValue(prefs::kHomePage)); - ASSERT_TRUE(expected->Equals(actual.get())); + ASSERT_TRUE(actual.get()); + EXPECT_TRUE(expected->Equals(actual.get())); +} + +TEST_F(ProfileSyncServicePreferenceTest, UpdatedSyncNodeActionUpdate) { + profile_->GetPrefs()->SetString(prefs::kHomePage, example_url0_); + CreateRootTask task(this, syncable::PREFERENCES); + ASSERT_TRUE(StartSyncService(&task, false)); + ASSERT_TRUE(task.success()); + + scoped_ptr<Value> expected(Value::CreateStringValue(example_url1_)); + ASSERT_NE(SetSyncedValue(prefs::kHomePage, *expected), sync_api::kInvalidId); + int64 node_id = model_associator_->GetSyncIdFromChromeId(prefs::kHomePage); + scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord); + record->action = SyncManager::ChangeRecord::ACTION_UPDATE; + record->id = node_id; + { + sync_api::WriteTransaction trans(backend()->GetUserShareHandle()); + change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1); + } + + const Value& actual = GetPreferenceValue(prefs::kHomePage); + EXPECT_TRUE(expected->Equals(&actual)); } -TEST_F(ProfileSyncServicePreferenceTest, UpdatedSyncNode) { - StartSyncService(); +TEST_F(ProfileSyncServicePreferenceTest, UpdatedSyncNodeActionAdd) { + CreateRootTask task(this, syncable::PREFERENCES); + ASSERT_TRUE(StartSyncService(&task, false)); + ASSERT_TRUE(task.success()); - scoped_ptr<Value> expected( - Value::CreateStringValue("http://example.com/test/bar")); - scoped_ptr<SyncManager::ChangeRecord> record( - SetSyncedValue(prefs::kHomePage, *expected)); + scoped_ptr<Value> expected(Value::CreateStringValue(example_url0_)); + int64 node_id = SetSyncedValue(prefs::kHomePage, *expected); + ASSERT_NE(node_id, sync_api::kInvalidId); + scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord); + record->action = SyncManager::ChangeRecord::ACTION_ADD; + record->id = node_id; { sync_api::WriteTransaction trans(backend()->GetUserShareHandle()); change_processor_->ApplyChangesFromSyncModel(&trans, record.get(), 1); } const Value& actual = GetPreferenceValue(prefs::kHomePage); - ASSERT_TRUE(expected->Equals(&actual)); + EXPECT_TRUE(expected->Equals(&actual)); + EXPECT_EQ(node_id, + model_associator_->GetSyncIdFromChromeId(prefs::kHomePage)); } diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index ab34683..8b06e38 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -913,6 +913,7 @@ 'browser/ssl/ssl_host_state_unittest.cc', 'browser/status_icons/status_icon_unittest.cc', 'browser/status_icons/status_tray_unittest.cc', + 'browser/sync/abstract_profile_sync_service_test.h', 'browser/sync/glue/autofill_data_type_controller_unittest.cc', 'browser/sync/glue/autofill_model_associator_unittest.cc', 'browser/sync/glue/bookmark_data_type_controller_unittest.cc', @@ -926,6 +927,7 @@ 'browser/sync/glue/extension_util_unittest.cc', 'browser/sync/glue/http_bridge_unittest.cc', 'browser/sync/glue/preference_data_type_controller_unittest.cc', + 'browser/sync/glue/preference_model_associator_unittest.cc', 'browser/sync/glue/sync_backend_host_mock.h', 'browser/sync/glue/theme_data_type_controller_unittest.cc', 'browser/sync/glue/theme_util_unittest.cc', |