| Index: chrome/browser/chromeos/preferences_unittest.cc | 
| diff --git a/chrome/browser/chromeos/preferences_unittest.cc b/chrome/browser/chromeos/preferences_unittest.cc | 
| index c8d6f9367018408335918c38b01e0031dbefd61e..3eda2391083e9a2af79faa2eb7233e38aeeba8ba 100644 | 
| --- a/chrome/browser/chromeos/preferences_unittest.cc | 
| +++ b/chrome/browser/chromeos/preferences_unittest.cc | 
| @@ -4,19 +4,49 @@ | 
|  | 
| #include "chrome/browser/chromeos/preferences.h" | 
|  | 
| +#include "base/json/json_string_value_serializer.h" | 
| #include "base/prefs/pref_member.h" | 
| +#include "chrome/browser/chromeos/input_method/input_method_configuration.h" | 
| #include "chrome/browser/chromeos/input_method/mock_input_method_manager.h" | 
| #include "chrome/browser/chromeos/login/users/fake_user_manager.h" | 
| #include "chrome/browser/chromeos/login/users/user_manager.h" | 
| +#include "chrome/browser/chromeos/system/fake_input_device_settings.h" | 
| #include "chrome/browser/download/download_prefs.h" | 
| #include "chrome/common/pref_names.h" | 
| #include "chrome/test/base/testing_pref_service_syncable.h" | 
| #include "components/pref_registry/pref_registry_syncable.h" | 
| +#include "sync/api/attachments/attachment_id.h" | 
| +#include "sync/api/attachments/attachment_service_proxy_for_test.h" | 
| +#include "sync/api/fake_sync_change_processor.h" | 
| +#include "sync/api/sync_change.h" | 
| +#include "sync/api/sync_data.h" | 
| +#include "sync/api/sync_error_factory.h" | 
| +#include "sync/api/sync_error_factory_mock.h" | 
| +#include "sync/api/syncable_service.h" | 
| +#include "sync/protocol/preference_specifics.pb.h" | 
| +#include "sync/protocol/sync.pb.h" | 
| #include "testing/gtest/include/gtest/gtest.h" | 
|  | 
| namespace chromeos { | 
| namespace { | 
|  | 
| +syncer::SyncData | 
| +CreatePrefSyncData(const std::string& name, const base::Value& value) { | 
| +  std::string serialized; | 
| +  JSONStringValueSerializer json(&serialized); | 
| +  json.Serialize(value); | 
| +  sync_pb::EntitySpecifics specifics; | 
| +  sync_pb::PreferenceSpecifics* pref = specifics.mutable_preference(); | 
| +  pref->set_name(name); | 
| +  pref->set_value(serialized); | 
| +  return syncer::SyncData::CreateRemoteData( | 
| +      1, | 
| +      specifics, | 
| +      base::Time(), | 
| +      syncer::AttachmentIdList(), | 
| +      syncer::AttachmentServiceProxyForTest::Create()); | 
| +} | 
| + | 
| class MyMockInputMethodManager : public input_method::MockInputMethodManager { | 
| public: | 
| MyMockInputMethodManager(StringPrefMember* previous, | 
| @@ -46,39 +76,346 @@ class MyMockInputMethodManager : public input_method::MockInputMethodManager { | 
|  | 
| }  // anonymous namespace | 
|  | 
| -TEST(PreferencesTest, TestUpdatePrefOnBrowserScreenDetails) { | 
| -  chromeos::FakeUserManager* user_manager = new chromeos::FakeUserManager(); | 
| -  chromeos::ScopedUserManagerEnabler user_manager_enabler(user_manager); | 
| -  const char test_user_email[] = "test_user@example.com"; | 
| -  const User* test_user = user_manager->AddUser(test_user_email); | 
| -  user_manager->LoginUser(test_user_email); | 
| - | 
| -  TestingPrefServiceSyncable prefs; | 
| -  Preferences::RegisterProfilePrefs(prefs.registry()); | 
| -  DownloadPrefs::RegisterProfilePrefs(prefs.registry()); | 
| -  // kSelectFileLastDirectory is registered for Profile. Here we register it for | 
| -  // testing. | 
| -  prefs.registry()->RegisterStringPref( | 
| -      prefs::kSelectFileLastDirectory, | 
| -      std::string(), | 
| -      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | 
| - | 
| -  StringPrefMember previous; | 
| -  previous.Init(prefs::kLanguagePreviousInputMethod, &prefs); | 
| -  previous.SetValue("KeyboardA"); | 
| -  StringPrefMember current; | 
| -  current.Init(prefs::kLanguageCurrentInputMethod, &prefs); | 
| -  current.SetValue("KeyboardB"); | 
| - | 
| -  MyMockInputMethodManager mock_manager(&previous, ¤t); | 
| -  Preferences testee(&mock_manager); | 
| -  testee.InitUserPrefsForTesting(&prefs, test_user); | 
| -  testee.SetInputMethodListForTesting(); | 
| - | 
| -  // Confirm they're unchanged. | 
| -  EXPECT_EQ("KeyboardA", previous.GetValue()); | 
| -  EXPECT_EQ("KeyboardB", current.GetValue()); | 
| -  EXPECT_EQ("KeyboardB", mock_manager.last_input_method_id_); | 
| +class PreferencesTest : public testing::Test { | 
| + public: | 
| +  PreferencesTest() {} | 
| +  virtual ~PreferencesTest() {} | 
| + | 
| +  virtual void SetUp() OVERRIDE { | 
| +    chromeos::FakeUserManager* user_manager = new chromeos::FakeUserManager(); | 
| +    user_manager_enabler_.reset( | 
| +        new chromeos::ScopedUserManagerEnabler(user_manager)); | 
| + | 
| +    const char test_user_email[] = "test_user@example.com"; | 
| +    test_user_ = user_manager->AddUser(test_user_email); | 
| +    user_manager->LoginUser(test_user_email); | 
| +    user_manager->SwitchActiveUser(test_user_email); | 
| + | 
| +    pref_service_.reset(new TestingPrefServiceSyncable); | 
| +    Preferences::RegisterProfilePrefs(pref_service_->registry()); | 
| +    DownloadPrefs::RegisterProfilePrefs(pref_service_->registry()); | 
| + | 
| +    // kSelectFileLastDirectory is registered for Profile. Here we register it | 
| +    // for testing. | 
| +    pref_service_->registry()->RegisterStringPref( | 
| +        prefs::kSelectFileLastDirectory, | 
| +        std::string(), | 
| +        user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | 
| + | 
| +    previous_input_method_.Init( | 
| +        prefs::kLanguagePreviousInputMethod, pref_service_.get()); | 
| +    previous_input_method_.SetValue("KeyboardA"); | 
| +    current_input_method_.Init( | 
| +        prefs::kLanguageCurrentInputMethod, pref_service_.get()); | 
| +    current_input_method_.SetValue("KeyboardB"); | 
| + | 
| +    mock_manager_ = new MyMockInputMethodManager(&previous_input_method_, | 
| +                                                 ¤t_input_method_); | 
| +    input_method::InitializeForTesting(mock_manager_); | 
| +    system::InputDeviceSettings::SetSettingsForTesting( | 
| +        new system::FakeInputDeviceSettings()); | 
| +    prefs_.reset(new Preferences(mock_manager_)); | 
| +  } | 
| + | 
| +  virtual void TearDown() OVERRIDE { | 
| +    input_method::Shutdown(); | 
| +  } | 
| + | 
| +  scoped_ptr<chromeos::ScopedUserManagerEnabler> user_manager_enabler_; | 
| +  const User* test_user_; | 
| +  scoped_ptr<TestingPrefServiceSyncable> pref_service_; | 
| +  StringPrefMember previous_input_method_; | 
| +  StringPrefMember current_input_method_; | 
| +  MyMockInputMethodManager* mock_manager_; | 
| +  scoped_ptr<Preferences> prefs_; | 
| + | 
| + private: | 
| +  DISALLOW_COPY_AND_ASSIGN(PreferencesTest); | 
| +}; | 
| + | 
| +TEST_F(PreferencesTest, TestUpdatePrefOnBrowserScreenDetails) { | 
| +  prefs_->Init(pref_service_.get(), test_user_); | 
| + | 
| +  // Confirm the current and previous input methods are unchanged. | 
| +  EXPECT_EQ("KeyboardA", previous_input_method_.GetValue()); | 
| +  EXPECT_EQ("KeyboardB", current_input_method_.GetValue()); | 
| +  EXPECT_EQ("KeyboardB", mock_manager_->last_input_method_id_); | 
| +} | 
| + | 
| +class InputMethodPreferencesTest : public PreferencesTest { | 
| + public: | 
| +  InputMethodPreferencesTest() {} | 
| +  virtual ~InputMethodPreferencesTest() {} | 
| + | 
| +  virtual void SetUp() OVERRIDE { | 
| +    PreferencesTest::SetUp(); | 
| + | 
| +    preferred_languages_.Init(prefs::kLanguagePreferredLanguages, | 
| +                              pref_service_.get()); | 
| +    preferred_languages_syncable_.Init( | 
| +        prefs::kLanguagePreferredLanguagesSyncable, | 
| +        pref_service_.get()); | 
| +    preload_engines_.Init(prefs::kLanguagePreloadEngines, pref_service_.get()); | 
| +    preload_engines_syncable_.Init(prefs::kLanguagePreloadEnginesSyncable, | 
| +                                   pref_service_.get()); | 
| +    enabled_extension_imes_.Init(prefs::kLanguageEnabledExtensionImes, | 
| +                                 pref_service_.get()); | 
| +    enabled_extension_imes_syncable_.Init( | 
| +        prefs::kLanguageEnabledExtensionImesSyncable, pref_service_.get()); | 
| +  } | 
| + | 
| +  // Helper function to set local language and input values. | 
| +  void SetLocalValues(const std::string& preferred_languages, | 
| +                      const std::string& preload_engines, | 
| +                      const std::string& enabled_extension_imes) { | 
| +    preferred_languages_.SetValue(preferred_languages); | 
| +    preload_engines_.SetValue(preload_engines); | 
| +    enabled_extension_imes_.SetValue(enabled_extension_imes); | 
| +  } | 
| + | 
| +  // Helper function to set global language and input values. | 
| +  void SetGlobalValues(const std::string& preferred_languages, | 
| +                      const std::string& preload_engines, | 
| +                      const std::string& enabled_extension_imes) { | 
| +    preferred_languages_syncable_.SetValue(preferred_languages); | 
| +    preload_engines_syncable_.SetValue(preload_engines); | 
| +    enabled_extension_imes_syncable_.SetValue(enabled_extension_imes); | 
| +  } | 
| + | 
| +  // Helper function to check local language and input values. | 
| +  void ExpectLocalValues(const std::string& preferred_languages, | 
| +                         const std::string& preload_engines, | 
| +                         const std::string& enabled_extension_imes) { | 
| +    EXPECT_EQ(preferred_languages, preferred_languages_.GetValue()); | 
| +    EXPECT_EQ(preload_engines, preload_engines_.GetValue()); | 
| +    EXPECT_EQ(enabled_extension_imes, enabled_extension_imes_.GetValue()); | 
| +  } | 
| + | 
| +  // Helper function to check global language and input values. | 
| +  void ExpectGlobalValues(const std::string& preferred_languages, | 
| +                          const std::string& preload_engines, | 
| +                          const std::string& enabled_extension_imes) { | 
| +    EXPECT_EQ(preferred_languages, preferred_languages_syncable_.GetValue()); | 
| +    EXPECT_EQ(preload_engines, preload_engines_syncable_.GetValue()); | 
| +    EXPECT_EQ(enabled_extension_imes, | 
| +              enabled_extension_imes_syncable_.GetValue()); | 
| +  } | 
| + | 
| +  StringPrefMember preferred_languages_; | 
| +  StringPrefMember preferred_languages_syncable_; | 
| +  StringPrefMember preload_engines_; | 
| +  StringPrefMember preload_engines_syncable_; | 
| +  StringPrefMember enabled_extension_imes_; | 
| +  StringPrefMember enabled_extension_imes_syncable_; | 
| + | 
| + private: | 
| +  DISALLOW_COPY_AND_ASSIGN(InputMethodPreferencesTest); | 
| +}; | 
| + | 
| +TEST_F(InputMethodPreferencesTest, TestOOBEAndSync) { | 
| +  // Choose options at OOBE. | 
| +  SetLocalValues("es", "xkb-old:es", ""); | 
| +  pref_service_->SetBoolean(prefs::kLanguageShouldMergeInputMethods, true); | 
| + | 
| +  // Initialize preferences. | 
| +  prefs_->Init(pref_service_.get(), test_user_); | 
| + | 
| +  // Add an input method before syncing starts. | 
| +  preload_engines_.SetValue("xkb-old:es,xkb-old:us"); | 
| + | 
| +  // Create some values to come from the server. | 
| +  syncer::SyncDataList sync_data_list; | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguagePreferredLanguagesSyncable, base::StringValue("ru,fi"))); | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguagePreloadEnginesSyncable, base::StringValue("xkb-old:ru"))); | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguageEnabledExtensionImesSyncable, base::StringValue("ime1"))); | 
| + | 
| +  // Sync for the first time. | 
| +  syncer::SyncableService* sync = | 
| +      pref_service_->GetSyncableService(syncer::PREFERENCES); | 
| +  sync->MergeDataAndStartSyncing(syncer::PREFERENCES, | 
| +                                 sync_data_list, | 
| +                                 scoped_ptr<syncer::SyncChangeProcessor>( | 
| +                                     new syncer::FakeSyncChangeProcessor), | 
| +                                 scoped_ptr<syncer::SyncErrorFactory>( | 
| +                                     new syncer::SyncErrorFactoryMock)); | 
| +  { | 
| +    SCOPED_TRACE("Server values should have merged with local values."); | 
| +    ExpectLocalValues("es,ru,fi", "xkb-old:es,xkb-old:us,xkb-old:ru", "ime1"); | 
| +  } | 
| + | 
| +  // Update the global values from the server again. | 
| +  syncer::SyncChangeList change_list; | 
| +  change_list.push_back(syncer::SyncChange( | 
| +      FROM_HERE, | 
| +      syncer::SyncChange::ACTION_UPDATE, | 
| +      CreatePrefSyncData( | 
| +          prefs::kLanguagePreferredLanguagesSyncable, | 
| +          base::StringValue("de")))); | 
| +  change_list.push_back(syncer::SyncChange( | 
| +      FROM_HERE, | 
| +      syncer::SyncChange::ACTION_UPDATE, | 
| +      CreatePrefSyncData( | 
| +          prefs::kLanguagePreloadEnginesSyncable, | 
| +          base::StringValue("xkb-old:de")))); | 
| +  change_list.push_back(syncer::SyncChange( | 
| +      FROM_HERE, | 
| +      syncer::SyncChange::ACTION_UPDATE, | 
| +      CreatePrefSyncData( | 
| +          prefs::kLanguageEnabledExtensionImesSyncable, | 
| +          base::StringValue("ime2")))); | 
| +  sync->ProcessSyncChanges(FROM_HERE, change_list); | 
| + | 
| +  { | 
| +    SCOPED_TRACE("Local preferences should have remained the same."); | 
| +    ExpectLocalValues("es,ru,fi", "xkb-old:es,xkb-old:us,xkb-old:ru", "ime1"); | 
| +  } | 
| +  { | 
| +    // Change local preferences. | 
| +    SCOPED_TRACE("Global preferences should have been updated."); | 
| +    SetLocalValues("jp", "xkb-old:jp", "ime2"); | 
| +    ExpectGlobalValues("jp", "xkb-old:jp", "ime2"); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(InputMethodPreferencesTest, TestLogIn) { | 
| +  // Set up existing preference values. | 
| +  SetLocalValues("es", "xkb-old:es", "ime1"); | 
| +  SetGlobalValues("es", "xkb-old:es", "ime1"); | 
| +  pref_service_->SetBoolean(prefs::kLanguageShouldMergeInputMethods, false); | 
| + | 
| +  // Initialize preferences. | 
| +  prefs_->Init(pref_service_.get(), test_user_); | 
| + | 
| +  // Create some values to come from the server. | 
| +  syncer::SyncDataList sync_data_list; | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguagePreferredLanguages, base::StringValue("ru,fi"))); | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguagePreloadEngines, base::StringValue("xkb-old:ru"))); | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguageEnabledExtensionImes, base::StringValue("ime1"))); | 
| + | 
| +  // Sync. | 
| +  syncer::SyncableService* sync = | 
| +      pref_service_->GetSyncableService(syncer::PREFERENCES); | 
| +  sync->MergeDataAndStartSyncing(syncer::PREFERENCES, | 
| +                                 sync_data_list, | 
| +                                 scoped_ptr<syncer::SyncChangeProcessor>( | 
| +                                     new syncer::FakeSyncChangeProcessor), | 
| +                                 scoped_ptr<syncer::SyncErrorFactory>( | 
| +                                     new syncer::SyncErrorFactoryMock)); | 
| +  { | 
| +    SCOPED_TRACE("Local preferences should have remained the same."); | 
| +    ExpectLocalValues("es", "xkb-old:es", "ime1"); | 
| +  } | 
| +  { | 
| +    // Change local preferences. | 
| +    SCOPED_TRACE("Global preferences should have been updated."); | 
| +    SetLocalValues("jp", "xkb-old:jp", "ime2"); | 
| +    ExpectGlobalValues("jp", "xkb-old:jp", "ime2"); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(InputMethodPreferencesTest, TestLogInDifferentGlobalValues) { | 
| +  // Set up existing preferences. | 
| +  SetLocalValues("es", "xkb-old:es", "ime1"); | 
| +  pref_service_->SetBoolean(prefs::kLanguageShouldMergeInputMethods, false); | 
| + | 
| +  // The global preferences have changed since we initialized the local prefs. | 
| +  SetGlobalValues("ru", "xkb-old:ru", "ime2"); | 
| + | 
| +  // Initialize preferences. | 
| +  prefs_->Init(pref_service_.get(), test_user_); | 
| +  { | 
| +    SCOPED_TRACE("Local preferences should have remained the same."); | 
| +    ExpectLocalValues("es", "xkb-old:es", "ime1"); | 
| +  } | 
| +  { | 
| +    // Change local preferences. | 
| +    SCOPED_TRACE("Global preferences should have been updated."); | 
| +    SetLocalValues("jp", "xkb-old:jp", "ime3"); | 
| +    ExpectGlobalValues("jp", "xkb-old:jp", "ime3"); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(InputMethodPreferencesTest, TestLogInLegacy) { | 
| +  // Simulate existing local preferences from M-36. | 
| +  SetLocalValues("es", "xkb-old:es", "ime1"); | 
| + | 
| +  // Initialize preferences. | 
| +  prefs_->Init(pref_service_.get(), test_user_); | 
| + | 
| +  // Sync. Since this is an existing profile, the local values don't change. | 
| +  syncer::SyncDataList sync_data_list; | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguagePreferredLanguagesSyncable, base::StringValue("ru,fi"))); | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguagePreloadEnginesSyncable, base::StringValue("xkb-old:ru"))); | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguageEnabledExtensionImesSyncable, base::StringValue("ime2"))); | 
| + | 
| +  syncer::SyncableService* sync = | 
| +      pref_service_->GetSyncableService(syncer::PREFERENCES); | 
| +  sync->MergeDataAndStartSyncing(syncer::PREFERENCES, | 
| +                                 sync_data_list, | 
| +                                 scoped_ptr<syncer::SyncChangeProcessor>( | 
| +                                     new syncer::FakeSyncChangeProcessor), | 
| +                                 scoped_ptr<syncer::SyncErrorFactory>( | 
| +                                     new syncer::SyncErrorFactoryMock)); | 
| +  { | 
| +    SCOPED_TRACE("Local preferences should have remained the same."); | 
| +    ExpectLocalValues("es", "xkb-old:es", "ime1"); | 
| +  } | 
| +  { | 
| +    // Change local preferences. | 
| +    SCOPED_TRACE("Global preferences should have been updated."); | 
| +    SetLocalValues("jp", "xkb-old:jp", "ime3"); | 
| +    ExpectGlobalValues("jp", "xkb-old:jp", "ime3"); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(InputMethodPreferencesTest, MergeStressTest) { | 
| +  SetLocalValues("es", "xkb-old:es,xkb-old:us,xkb-old:jp", ""); | 
| +  pref_service_->SetBoolean(prefs::kLanguageShouldMergeInputMethods, true); | 
| + | 
| +  // Initialize preferences. | 
| +  prefs_->Init(pref_service_.get(), test_user_); | 
| + | 
| +  // Change input methods and languages before syncing starts. | 
| +  SetLocalValues("en,es,jp,ar", | 
| +                 "xkb-old:es,xkb-old:jp,xkb-old:dv,xkb-old:ar", | 
| +                 "ime2,ime1,ime4"); | 
| + | 
| +  // Create some tricky values to come from the server. | 
| +  syncer::SyncDataList sync_data_list; | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguagePreferredLanguagesSyncable, | 
| +      base::StringValue("ar,fi,es,de,ar"))); | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguagePreloadEnginesSyncable, | 
| +      base::StringValue("xkb-old:ru,xkb-old:ru,xkb-old:jp,xkb-old:jp"))); | 
| +  sync_data_list.push_back(CreatePrefSyncData( | 
| +      prefs::kLanguageEnabledExtensionImesSyncable, | 
| +      base::StringValue(""))); | 
| + | 
| +  // Sync for the first time. | 
| +  syncer::SyncableService* sync = | 
| +      pref_service_->GetSyncableService(syncer::PREFERENCES); | 
| +  sync->MergeDataAndStartSyncing(syncer::PREFERENCES, | 
| +                                 sync_data_list, | 
| +                                 scoped_ptr<syncer::SyncChangeProcessor>( | 
| +                                     new syncer::FakeSyncChangeProcessor), | 
| +                                 scoped_ptr<syncer::SyncErrorFactory>( | 
| +                                     new syncer::SyncErrorFactoryMock)); | 
| +  { | 
| +    SCOPED_TRACE("Server values should have merged with local values."); | 
| +    ExpectLocalValues("en,es,jp,ar,fi,de", | 
| +                      "xkb-old:es,xkb-old:jp,xkb-old:dv,xkb-old:ar,xkb-old:ru", | 
| +                      "ime2,ime1,ime4"); | 
| +  } | 
| } | 
|  | 
| }  // namespace chromeos | 
|  |