1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/prefs/pref_notifier_impl.h"
6#include "chrome/browser/prefs/pref_observer_mock.h"
7#include "chrome/browser/prefs/pref_service.h"
8#include "chrome/browser/prefs/pref_value_store.h"
9#include "chrome/test/testing_pref_service.h"
10#include "content/common/notification_observer_mock.h"
11#include "content/common/notification_registrar.h"
12#include "content/common/notification_service.h"
13#include "testing/gmock/include/gmock/gmock.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16using testing::_;
17using testing::Field;
18using testing::Invoke;
19using testing::Mock;
20using testing::Truly;
21
22namespace {
23
24const char kChangedPref[] = "changed_pref";
25const char kUnchangedPref[] = "unchanged_pref";
26
27bool DetailsAreChangedPref(const Details<std::string>& details) {
28  std::string* string_in = Details<std::string>(details).ptr();
29  return strcmp(string_in->c_str(), kChangedPref) == 0;
30}
31
32// Test PrefNotifier that allows tracking of observers and notifications.
33class MockPrefNotifier : public PrefNotifierImpl {
34 public:
35  explicit MockPrefNotifier(PrefService* pref_service)
36      : PrefNotifierImpl(pref_service) {}
37  virtual ~MockPrefNotifier() {}
38
39  MOCK_METHOD1(FireObservers, void(const std::string& path));
40
41  size_t CountObserver(const char* path, NotificationObserver* obs) {
42    PrefObserverMap::const_iterator observer_iterator =
43        pref_observers()->find(path);
44    if (observer_iterator == pref_observers()->end())
45      return false;
46
47    NotificationObserverList* observer_list = observer_iterator->second;
48    NotificationObserverList::Iterator it(*observer_list);
49    NotificationObserver* existing_obs;
50    size_t count = 0;
51    while ((existing_obs = it.GetNext()) != NULL) {
52      if (existing_obs == obs)
53        count++;
54    }
55
56    return count;
57  }
58};
59
60// Test fixture class.
61class PrefNotifierTest : public testing::Test {
62 protected:
63  virtual void SetUp() {
64    pref_service_.RegisterBooleanPref(kChangedPref, true);
65    pref_service_.RegisterBooleanPref(kUnchangedPref, true);
66  }
67
68  TestingPrefService pref_service_;
69
70  PrefObserverMock obs1_;
71  PrefObserverMock obs2_;
72};
73
74TEST_F(PrefNotifierTest, OnPreferenceChanged) {
75  MockPrefNotifier notifier(&pref_service_);
76  EXPECT_CALL(notifier, FireObservers(kChangedPref)).Times(1);
77  notifier.OnPreferenceChanged(kChangedPref);
78}
79
80TEST_F(PrefNotifierTest, OnInitializationCompleted) {
81  MockPrefNotifier notifier(&pref_service_);
82  NotificationObserverMock observer;
83  NotificationRegistrar registrar;
84  registrar.Add(&observer, NotificationType::PREF_INITIALIZATION_COMPLETED,
85                Source<PrefService>(&pref_service_));
86  EXPECT_CALL(observer, Observe(
87      Field(&NotificationType::value,
88            NotificationType::PREF_INITIALIZATION_COMPLETED),
89      Source<PrefService>(&pref_service_),
90      NotificationService::NoDetails()));
91  notifier.OnInitializationCompleted();
92}
93
94TEST_F(PrefNotifierTest, AddAndRemovePrefObservers) {
95  const char pref_name[] = "homepage";
96  const char pref_name2[] = "proxy";
97
98  MockPrefNotifier notifier(&pref_service_);
99  notifier.AddPrefObserver(pref_name, &obs1_);
100  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
101  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_));
102  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
103  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
104
105  // Re-adding the same observer for the same pref doesn't change anything.
106  // Skip this in debug mode, since it hits a DCHECK and death tests aren't
107  // thread-safe.
108#if defined(NDEBUG)
109  notifier.AddPrefObserver(pref_name, &obs1_);
110  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
111  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_));
112  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
113  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
114#endif  // NDEBUG
115
116  // Ensure that we can add the same observer to a different pref.
117  notifier.AddPrefObserver(pref_name2, &obs1_);
118  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
119  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
120  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
121  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
122
123  // Ensure that we can add another observer to the same pref.
124  notifier.AddPrefObserver(pref_name, &obs2_);
125  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
126  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
127  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_));
128  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
129
130  // Ensure that we can remove all observers, and that removing a non-existent
131  // observer is harmless.
132  notifier.RemovePrefObserver(pref_name, &obs1_);
133  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
134  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
135  ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_));
136  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
137
138  notifier.RemovePrefObserver(pref_name, &obs2_);
139  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
140  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
141  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
142  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
143
144  notifier.RemovePrefObserver(pref_name, &obs1_);
145  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
146  ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
147  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
148  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
149
150  notifier.RemovePrefObserver(pref_name2, &obs1_);
151  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
152  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_));
153  ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
154  ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
155}
156
157TEST_F(PrefNotifierTest, FireObservers) {
158  FundamentalValue value_true(true);
159  PrefNotifierImpl notifier(&pref_service_);
160  notifier.AddPrefObserver(kChangedPref, &obs1_);
161  notifier.AddPrefObserver(kUnchangedPref, &obs1_);
162
163  obs1_.Expect(&pref_service_, kChangedPref, &value_true);
164  EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0);
165  notifier.OnPreferenceChanged(kChangedPref);
166  Mock::VerifyAndClearExpectations(&obs1_);
167  Mock::VerifyAndClearExpectations(&obs2_);
168
169  notifier.AddPrefObserver(kChangedPref, &obs2_);
170  notifier.AddPrefObserver(kUnchangedPref, &obs2_);
171
172  obs1_.Expect(&pref_service_, kChangedPref, &value_true);
173  obs2_.Expect(&pref_service_, kChangedPref, &value_true);
174  notifier.OnPreferenceChanged(kChangedPref);
175  Mock::VerifyAndClearExpectations(&obs1_);
176  Mock::VerifyAndClearExpectations(&obs2_);
177
178  // Make sure removing an observer from one pref doesn't affect anything else.
179  notifier.RemovePrefObserver(kChangedPref, &obs1_);
180
181  EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0);
182  obs2_.Expect(&pref_service_, kChangedPref, &value_true);
183  notifier.OnPreferenceChanged(kChangedPref);
184  Mock::VerifyAndClearExpectations(&obs1_);
185  Mock::VerifyAndClearExpectations(&obs2_);
186
187  // Make sure removing an observer entirely doesn't affect anything else.
188  notifier.RemovePrefObserver(kUnchangedPref, &obs1_);
189
190  EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0);
191  obs2_.Expect(&pref_service_, kChangedPref, &value_true);
192  notifier.OnPreferenceChanged(kChangedPref);
193  Mock::VerifyAndClearExpectations(&obs1_);
194  Mock::VerifyAndClearExpectations(&obs2_);
195
196  notifier.RemovePrefObserver(kChangedPref, &obs2_);
197  notifier.RemovePrefObserver(kUnchangedPref, &obs2_);
198}
199
200}  // namespace
201