| // Copyright (c) 2011 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 "components/prefs/pref_member.h" | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/location.h" | 
 | #include "base/single_thread_task_runner.h" | 
 | #include "base/synchronization/waitable_event.h" | 
 | #include "base/threading/thread.h" | 
 | #include "components/prefs/pref_registry_simple.h" | 
 | #include "components/prefs/testing_pref_service.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace { | 
 |  | 
 | const char kBoolPref[] = "bool"; | 
 | const char kIntPref[] = "int"; | 
 | const char kDoublePref[] = "double"; | 
 | const char kStringPref[] = "string"; | 
 | const char kStringListPref[] = "string_list"; | 
 |  | 
 | void RegisterTestPrefs(PrefRegistrySimple* registry) { | 
 |   registry->RegisterBooleanPref(kBoolPref, false); | 
 |   registry->RegisterIntegerPref(kIntPref, 0); | 
 |   registry->RegisterDoublePref(kDoublePref, 0.0); | 
 |   registry->RegisterStringPref(kStringPref, "default"); | 
 |   registry->RegisterListPref(kStringListPref, new base::ListValue()); | 
 | } | 
 |  | 
 | class GetPrefValueHelper | 
 |     : public base::RefCountedThreadSafe<GetPrefValueHelper> { | 
 |  public: | 
 |   GetPrefValueHelper() : value_(false), pref_thread_("pref thread") { | 
 |     pref_thread_.Start(); | 
 |   } | 
 |  | 
 |   void Init(const std::string& pref_name, PrefService* prefs) { | 
 |     pref_.Init(pref_name, prefs); | 
 |     pref_.MoveToThread(pref_thread_.task_runner()); | 
 |   } | 
 |  | 
 |   void Destroy() { | 
 |     pref_.Destroy(); | 
 |   } | 
 |  | 
 |   void FetchValue() { | 
 |     base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, | 
 |                               base::WaitableEvent::InitialState::NOT_SIGNALED); | 
 |     ASSERT_TRUE(pref_thread_.task_runner()->PostTask( | 
 |         FROM_HERE, | 
 |         base::Bind(&GetPrefValueHelper::GetPrefValue, this, &event))); | 
 |     event.Wait(); | 
 |   } | 
 |  | 
 |   // The thread must be stopped on the main thread. GetPrefValueHelper being | 
 |   // ref-counted, the destructor can be called from any thread. | 
 |   void StopThread() { | 
 |     pref_thread_.Stop(); | 
 |   } | 
 |  | 
 |   bool value() { return value_; } | 
 |  | 
 |  private: | 
 |   friend class base::RefCountedThreadSafe<GetPrefValueHelper>; | 
 |   ~GetPrefValueHelper() {} | 
 |  | 
 |   void GetPrefValue(base::WaitableEvent* event) { | 
 |     value_ = pref_.GetValue(); | 
 |     event->Signal(); | 
 |   } | 
 |  | 
 |   BooleanPrefMember pref_; | 
 |   bool value_; | 
 |  | 
 |   base::Thread pref_thread_;  // The thread |pref_| runs on. | 
 | }; | 
 |  | 
 | class PrefMemberTestClass { | 
 |  public: | 
 |   explicit PrefMemberTestClass(PrefService* prefs) | 
 |       : observe_cnt_(0), prefs_(prefs) { | 
 |     str_.Init(kStringPref, prefs, | 
 |               base::Bind(&PrefMemberTestClass::OnPreferenceChanged, | 
 |                          base::Unretained(this))); | 
 |   } | 
 |  | 
 |   void OnPreferenceChanged(const std::string& pref_name) { | 
 |     EXPECT_EQ(pref_name, kStringPref); | 
 |     EXPECT_EQ(str_.GetValue(), prefs_->GetString(kStringPref)); | 
 |     ++observe_cnt_; | 
 |   } | 
 |  | 
 |   StringPrefMember str_; | 
 |   int observe_cnt_; | 
 |  | 
 |  private: | 
 |   PrefService* prefs_; | 
 | }; | 
 |  | 
 | }  // anonymous namespace | 
 |  | 
 | class PrefMemberTest : public testing::Test { | 
 |   base::MessageLoop message_loop_; | 
 | }; | 
 |  | 
 | TEST_F(PrefMemberTest, BasicGetAndSet) { | 
 |   TestingPrefServiceSimple prefs; | 
 |   RegisterTestPrefs(prefs.registry()); | 
 |  | 
 |   // Test bool | 
 |   BooleanPrefMember boolean; | 
 |   boolean.Init(kBoolPref, &prefs); | 
 |  | 
 |   // Check the defaults | 
 |   EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); | 
 |   EXPECT_FALSE(boolean.GetValue()); | 
 |   EXPECT_FALSE(*boolean); | 
 |  | 
 |   // Try changing through the member variable. | 
 |   boolean.SetValue(true); | 
 |   EXPECT_TRUE(boolean.GetValue()); | 
 |   EXPECT_TRUE(prefs.GetBoolean(kBoolPref)); | 
 |   EXPECT_TRUE(*boolean); | 
 |  | 
 |   // Try changing back through the pref. | 
 |   prefs.SetBoolean(kBoolPref, false); | 
 |   EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); | 
 |   EXPECT_FALSE(boolean.GetValue()); | 
 |   EXPECT_FALSE(*boolean); | 
 |  | 
 |   // Test int | 
 |   IntegerPrefMember integer; | 
 |   integer.Init(kIntPref, &prefs); | 
 |  | 
 |   // Check the defaults | 
 |   EXPECT_EQ(0, prefs.GetInteger(kIntPref)); | 
 |   EXPECT_EQ(0, integer.GetValue()); | 
 |   EXPECT_EQ(0, *integer); | 
 |  | 
 |   // Try changing through the member variable. | 
 |   integer.SetValue(5); | 
 |   EXPECT_EQ(5, integer.GetValue()); | 
 |   EXPECT_EQ(5, prefs.GetInteger(kIntPref)); | 
 |   EXPECT_EQ(5, *integer); | 
 |  | 
 |   // Try changing back through the pref. | 
 |   prefs.SetInteger(kIntPref, 2); | 
 |   EXPECT_EQ(2, prefs.GetInteger(kIntPref)); | 
 |   EXPECT_EQ(2, integer.GetValue()); | 
 |   EXPECT_EQ(2, *integer); | 
 |  | 
 |   // Test double | 
 |   DoublePrefMember double_member; | 
 |   double_member.Init(kDoublePref, &prefs); | 
 |  | 
 |   // Check the defaults | 
 |   EXPECT_EQ(0.0, prefs.GetDouble(kDoublePref)); | 
 |   EXPECT_EQ(0.0, double_member.GetValue()); | 
 |   EXPECT_EQ(0.0, *double_member); | 
 |  | 
 |   // Try changing through the member variable. | 
 |   double_member.SetValue(1.0); | 
 |   EXPECT_EQ(1.0, double_member.GetValue()); | 
 |   EXPECT_EQ(1.0, prefs.GetDouble(kDoublePref)); | 
 |   EXPECT_EQ(1.0, *double_member); | 
 |  | 
 |   // Try changing back through the pref. | 
 |   prefs.SetDouble(kDoublePref, 3.0); | 
 |   EXPECT_EQ(3.0, prefs.GetDouble(kDoublePref)); | 
 |   EXPECT_EQ(3.0, double_member.GetValue()); | 
 |   EXPECT_EQ(3.0, *double_member); | 
 |  | 
 |   // Test string | 
 |   StringPrefMember string; | 
 |   string.Init(kStringPref, &prefs); | 
 |  | 
 |   // Check the defaults | 
 |   EXPECT_EQ("default", prefs.GetString(kStringPref)); | 
 |   EXPECT_EQ("default", string.GetValue()); | 
 |   EXPECT_EQ("default", *string); | 
 |  | 
 |   // Try changing through the member variable. | 
 |   string.SetValue("foo"); | 
 |   EXPECT_EQ("foo", string.GetValue()); | 
 |   EXPECT_EQ("foo", prefs.GetString(kStringPref)); | 
 |   EXPECT_EQ("foo", *string); | 
 |  | 
 |   // Try changing back through the pref. | 
 |   prefs.SetString(kStringPref, "bar"); | 
 |   EXPECT_EQ("bar", prefs.GetString(kStringPref)); | 
 |   EXPECT_EQ("bar", string.GetValue()); | 
 |   EXPECT_EQ("bar", *string); | 
 |  | 
 |   // Test string list | 
 |   base::ListValue expected_list; | 
 |   std::vector<std::string> expected_vector; | 
 |   StringListPrefMember string_list; | 
 |   string_list.Init(kStringListPref, &prefs); | 
 |  | 
 |   // Check the defaults | 
 |   EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); | 
 |   EXPECT_EQ(expected_vector, string_list.GetValue()); | 
 |   EXPECT_EQ(expected_vector, *string_list); | 
 |  | 
 |   // Try changing through the pref member. | 
 |   expected_list.AppendString("foo"); | 
 |   expected_vector.push_back("foo"); | 
 |   string_list.SetValue(expected_vector); | 
 |  | 
 |   EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); | 
 |   EXPECT_EQ(expected_vector, string_list.GetValue()); | 
 |   EXPECT_EQ(expected_vector, *string_list); | 
 |  | 
 |   // Try adding through the pref. | 
 |   expected_list.AppendString("bar"); | 
 |   expected_vector.push_back("bar"); | 
 |   prefs.Set(kStringListPref, expected_list); | 
 |  | 
 |   EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); | 
 |   EXPECT_EQ(expected_vector, string_list.GetValue()); | 
 |   EXPECT_EQ(expected_vector, *string_list); | 
 |  | 
 |   // Try removing through the pref. | 
 |   expected_list.Remove(0, NULL); | 
 |   expected_vector.erase(expected_vector.begin()); | 
 |   prefs.Set(kStringListPref, expected_list); | 
 |  | 
 |   EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref))); | 
 |   EXPECT_EQ(expected_vector, string_list.GetValue()); | 
 |   EXPECT_EQ(expected_vector, *string_list); | 
 | } | 
 |  | 
 | TEST_F(PrefMemberTest, InvalidList) { | 
 |   // Set the vector to an initial good value. | 
 |   std::vector<std::string> expected_vector; | 
 |   expected_vector.push_back("foo"); | 
 |  | 
 |   // Try to add a valid list first. | 
 |   base::ListValue list; | 
 |   list.AppendString("foo"); | 
 |   std::vector<std::string> vector; | 
 |   EXPECT_TRUE(subtle::PrefMemberVectorStringUpdate(list, &vector)); | 
 |   EXPECT_EQ(expected_vector, vector); | 
 |  | 
 |   // Now try to add an invalid list.  |vector| should not be changed. | 
 |   list.AppendInteger(0); | 
 |   EXPECT_FALSE(subtle::PrefMemberVectorStringUpdate(list, &vector)); | 
 |   EXPECT_EQ(expected_vector, vector); | 
 | } | 
 |  | 
 | TEST_F(PrefMemberTest, TwoPrefs) { | 
 |   // Make sure two DoublePrefMembers stay in sync. | 
 |   TestingPrefServiceSimple prefs; | 
 |   RegisterTestPrefs(prefs.registry()); | 
 |  | 
 |   DoublePrefMember pref1; | 
 |   pref1.Init(kDoublePref, &prefs); | 
 |   DoublePrefMember pref2; | 
 |   pref2.Init(kDoublePref, &prefs); | 
 |  | 
 |   pref1.SetValue(2.3); | 
 |   EXPECT_EQ(2.3, *pref2); | 
 |  | 
 |   pref2.SetValue(3.5); | 
 |   EXPECT_EQ(3.5, *pref1); | 
 |  | 
 |   prefs.SetDouble(kDoublePref, 4.2); | 
 |   EXPECT_EQ(4.2, *pref1); | 
 |   EXPECT_EQ(4.2, *pref2); | 
 | } | 
 |  | 
 | TEST_F(PrefMemberTest, Observer) { | 
 |   TestingPrefServiceSimple prefs; | 
 |   RegisterTestPrefs(prefs.registry()); | 
 |  | 
 |   PrefMemberTestClass test_obj(&prefs); | 
 |   EXPECT_EQ("default", *test_obj.str_); | 
 |  | 
 |   // Calling SetValue should not fire the observer. | 
 |   test_obj.str_.SetValue("hello"); | 
 |   EXPECT_EQ(0, test_obj.observe_cnt_); | 
 |   EXPECT_EQ("hello", prefs.GetString(kStringPref)); | 
 |  | 
 |   // Changing the pref does fire the observer. | 
 |   prefs.SetString(kStringPref, "world"); | 
 |   EXPECT_EQ(1, test_obj.observe_cnt_); | 
 |   EXPECT_EQ("world", *(test_obj.str_)); | 
 |  | 
 |   // Not changing the value should not fire the observer. | 
 |   prefs.SetString(kStringPref, "world"); | 
 |   EXPECT_EQ(1, test_obj.observe_cnt_); | 
 |   EXPECT_EQ("world", *(test_obj.str_)); | 
 |  | 
 |   prefs.SetString(kStringPref, "hello"); | 
 |   EXPECT_EQ(2, test_obj.observe_cnt_); | 
 |   EXPECT_EQ("hello", prefs.GetString(kStringPref)); | 
 | } | 
 |  | 
 | TEST_F(PrefMemberTest, NoInit) { | 
 |   // Make sure not calling Init on a PrefMember doesn't cause problems. | 
 |   IntegerPrefMember pref; | 
 | } | 
 |  | 
 | TEST_F(PrefMemberTest, MoveToThread) { | 
 |   TestingPrefServiceSimple prefs; | 
 |   scoped_refptr<GetPrefValueHelper> helper(new GetPrefValueHelper()); | 
 |   RegisterTestPrefs(prefs.registry()); | 
 |   helper->Init(kBoolPref, &prefs); | 
 |  | 
 |   helper->FetchValue(); | 
 |   EXPECT_FALSE(helper->value()); | 
 |  | 
 |   prefs.SetBoolean(kBoolPref, true); | 
 |  | 
 |   helper->FetchValue(); | 
 |   EXPECT_TRUE(helper->value()); | 
 |  | 
 |   helper->Destroy(); | 
 |  | 
 |   helper->FetchValue(); | 
 |   EXPECT_TRUE(helper->value()); | 
 |  | 
 |   helper->StopThread(); | 
 | } |