1baa3858d3f5d128a5c8466b700098109edcad5f2repo sync// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2baa3858d3f5d128a5c8466b700098109edcad5f2repo sync// Use of this source code is governed by a BSD-style license that can be
3baa3858d3f5d128a5c8466b700098109edcad5f2repo sync// found in the LICENSE file.
4baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
5baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "base/compiler_specific.h"
6baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "base/strings/string16.h"
7baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "base/strings/utf_string_conversions.h"
8baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "chrome/browser/profiles/profile.h"
9baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "chrome/browser/search_engines/template_url_service_factory_test_util.h"
10baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
11baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "chrome/browser/ui/search_engines/template_url_table_model.h"
12baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "chrome/test/base/testing_pref_service_syncable.h"
13baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "chrome/test/base/testing_profile.h"
14baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "components/search_engines/template_url.h"
15baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "components/search_engines/template_url_service.h"
16baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "content/public/test/test_browser_thread_bundle.h"
17baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "testing/gtest/include/gtest/gtest.h"
18baa3858d3f5d128a5c8466b700098109edcad5f2repo sync#include "ui/base/models/table_model_observer.h"
19baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
20baa3858d3f5d128a5c8466b700098109edcad5f2repo syncusing base::ASCIIToUTF16;
21baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
22baa3858d3f5d128a5c8466b700098109edcad5f2repo syncstatic const base::string16 kA(ASCIIToUTF16("a"));
23baa3858d3f5d128a5c8466b700098109edcad5f2repo syncstatic const base::string16 kA1(ASCIIToUTF16("a1"));
24baa3858d3f5d128a5c8466b700098109edcad5f2repo syncstatic const base::string16 kB(ASCIIToUTF16("b"));
25baa3858d3f5d128a5c8466b700098109edcad5f2repo syncstatic const base::string16 kB1(ASCIIToUTF16("b1"));
26baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
27baa3858d3f5d128a5c8466b700098109edcad5f2repo sync// Base class for keyword editor tests. Creates a profile containing an
28baa3858d3f5d128a5c8466b700098109edcad5f2repo sync// empty TemplateURLService.
29baa3858d3f5d128a5c8466b700098109edcad5f2repo syncclass KeywordEditorControllerTest : public testing::Test,
30baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                                    public ui::TableModelObserver {
31baa3858d3f5d128a5c8466b700098109edcad5f2repo sync public:
32baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  KeywordEditorControllerTest()
33baa3858d3f5d128a5c8466b700098109edcad5f2repo sync      : util_(&profile_),
34baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        simulate_load_failure_(false),
35baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        model_changed_count_(0),
36baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        items_changed_count_(0),
37baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        added_count_(0),
38baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        removed_count_(0) {}
39baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
40baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  explicit KeywordEditorControllerTest(bool simulate_load_failure)
41baa3858d3f5d128a5c8466b700098109edcad5f2repo sync      : util_(&profile_),
42baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        simulate_load_failure_(simulate_load_failure),
43baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        model_changed_count_(0),
44baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        items_changed_count_(0),
45baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        added_count_(0),
46baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        removed_count_(0) {}
47baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
48baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  virtual void SetUp() OVERRIDE {
49baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    if (simulate_load_failure_)
50baa3858d3f5d128a5c8466b700098109edcad5f2repo sync      util_.model()->OnWebDataServiceRequestDone(0, NULL);
51baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    else
52baa3858d3f5d128a5c8466b700098109edcad5f2repo sync      util_.VerifyLoad();
53baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
54baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    controller_.reset(new KeywordEditorController(&profile_));
55baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    controller_->table_model()->SetObserver(this);
56baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  }
57baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
58baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  virtual void TearDown() OVERRIDE {
59baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    controller_.reset();
60baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  }
61baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
62baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  virtual void OnModelChanged() OVERRIDE {
63baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    model_changed_count_++;
64baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  }
65baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
66baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  virtual void OnItemsChanged(int start, int length) OVERRIDE {
67baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    items_changed_count_++;
68baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  }
69baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
70baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  virtual void OnItemsAdded(int start, int length) OVERRIDE {
71baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    added_count_++;
72baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  }
73baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
74baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  virtual void OnItemsRemoved(int start, int length) OVERRIDE {
75baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    removed_count_++;
76baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  }
77baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
78baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  void VerifyChangeCount(int model_changed_count, int item_changed_count,
79baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                         int added_count, int removed_count) {
80baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    ASSERT_EQ(model_changed_count, model_changed_count_);
81baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    ASSERT_EQ(item_changed_count, items_changed_count_);
82baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    ASSERT_EQ(added_count, added_count_);
83baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    ASSERT_EQ(removed_count, removed_count_);
84baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    ClearChangeCount();
85baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  }
86baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
87baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  void ClearChangeCount() {
88baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    model_changed_count_ = items_changed_count_ = added_count_ =
89baa3858d3f5d128a5c8466b700098109edcad5f2repo sync        removed_count_ = 0;
90baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  }
91baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
92baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  void SimulateDefaultSearchIsManaged(const std::string& url) {
93baa3858d3f5d128a5c8466b700098109edcad5f2repo sync    util_.SetManagedDefaultSearchPreferences(true,
94baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                                             "managed",
95baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                                             "managed",
96baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                                             url,
97baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                                             std::string(),
98baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                                             std::string(),
99baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                                             std::string(),
100baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                                             std::string(),
101baa3858d3f5d128a5c8466b700098109edcad5f2repo sync                                             std::string());
102baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  }
103baa3858d3f5d128a5c8466b700098109edcad5f2repo sync
104baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  TemplateURLTableModel* table_model() { return controller_->table_model(); }
105baa3858d3f5d128a5c8466b700098109edcad5f2repo sync  KeywordEditorController* controller() { return controller_.get(); }
106  const TemplateURLServiceFactoryTestUtil* util() const { return &util_; }
107
108  int items_changed_count() const { return items_changed_count_; }
109  int added_count() const { return added_count_; }
110  int removed_count() const { return removed_count_; }
111
112 private:
113  content::TestBrowserThreadBundle thread_bundle_;
114  TestingProfile profile_;
115  scoped_ptr<KeywordEditorController> controller_;
116  TemplateURLServiceFactoryTestUtil util_;
117  bool simulate_load_failure_;
118
119  int model_changed_count_;
120  int items_changed_count_;
121  int added_count_;
122  int removed_count_;
123};
124
125class KeywordEditorControllerNoWebDataTest
126    : public KeywordEditorControllerTest {
127 public:
128  KeywordEditorControllerNoWebDataTest() : KeywordEditorControllerTest(true) {}
129};
130
131// Tests adding a TemplateURL.
132TEST_F(KeywordEditorControllerTest, Add) {
133  int original_row_count = table_model()->RowCount();
134  controller()->AddTemplateURL(kA, kB, "http://c");
135
136  // Verify the observer was notified.
137  VerifyChangeCount(0, 0, 1, 0);
138  if (HasFatalFailure())
139    return;
140
141  // Verify the TableModel has the new data.
142  ASSERT_EQ(original_row_count + 1, table_model()->RowCount());
143
144  // Verify the TemplateURLService has the new data.
145  const TemplateURL* turl = util()->model()->GetTemplateURLForKeyword(kB);
146  ASSERT_TRUE(turl);
147  EXPECT_EQ(ASCIIToUTF16("a"), turl->short_name());
148  EXPECT_EQ(ASCIIToUTF16("b"), turl->keyword());
149  EXPECT_EQ("http://c", turl->url());
150}
151
152// Tests modifying a TemplateURL.
153TEST_F(KeywordEditorControllerTest, Modify) {
154  controller()->AddTemplateURL(kA, kB, "http://c");
155  ClearChangeCount();
156
157  // Modify the entry.
158  TemplateURL* turl = util()->model()->GetTemplateURLs()[0];
159  controller()->ModifyTemplateURL(turl, kA1, kB1, "http://c1");
160
161  // Make sure it was updated appropriately.
162  VerifyChangeCount(0, 1, 0, 0);
163  EXPECT_EQ(ASCIIToUTF16("a1"), turl->short_name());
164  EXPECT_EQ(ASCIIToUTF16("b1"), turl->keyword());
165  EXPECT_EQ("http://c1", turl->url());
166}
167
168// Tests making a TemplateURL the default search provider.
169TEST_F(KeywordEditorControllerTest, MakeDefault) {
170  int index = controller()->AddTemplateURL(kA, kB, "http://c{searchTerms}");
171  ClearChangeCount();
172
173  const TemplateURL* turl = util()->model()->GetTemplateURLForKeyword(kB);
174  int new_default = controller()->MakeDefaultTemplateURL(index);
175  EXPECT_EQ(index, new_default);
176  // Making an item the default sends a handful of changes. Which are sent isn't
177  // important, what is important is 'something' is sent.
178  ASSERT_TRUE(items_changed_count() > 0 || added_count() > 0 ||
179              removed_count() > 0);
180  ASSERT_TRUE(util()->model()->GetDefaultSearchProvider() == turl);
181
182  // Making it default a second time should fail.
183  new_default = controller()->MakeDefaultTemplateURL(index);
184  EXPECT_EQ(-1, new_default);
185}
186
187// Tests that a TemplateURL can't be made the default if the default search
188// provider is managed via policy.
189TEST_F(KeywordEditorControllerTest, CannotSetDefaultWhileManaged) {
190  controller()->AddTemplateURL(kA, kB, "http://c{searchTerms}");
191  controller()->AddTemplateURL(kA1, kB1, "http://d{searchTerms}");
192  ClearChangeCount();
193
194  const TemplateURL* turl1 =
195      util()->model()->GetTemplateURLForKeyword(ASCIIToUTF16("b"));
196  ASSERT_TRUE(turl1 != NULL);
197  const TemplateURL* turl2 =
198      util()->model()->GetTemplateURLForKeyword(ASCIIToUTF16("b1"));
199  ASSERT_TRUE(turl2 != NULL);
200
201  EXPECT_TRUE(controller()->CanMakeDefault(turl1));
202  EXPECT_TRUE(controller()->CanMakeDefault(turl2));
203
204  SimulateDefaultSearchIsManaged(turl2->url());
205  EXPECT_TRUE(util()->model()->is_default_search_managed());
206
207  EXPECT_FALSE(controller()->CanMakeDefault(turl1));
208  EXPECT_FALSE(controller()->CanMakeDefault(turl2));
209}
210
211// Tests that a TemplateURL can't be edited if it is the managed default search
212// provider.
213TEST_F(KeywordEditorControllerTest, EditManagedDefault) {
214  controller()->AddTemplateURL(kA, kB, "http://c{searchTerms}");
215  controller()->AddTemplateURL(kA1, kB1, "http://d{searchTerms}");
216  ClearChangeCount();
217
218  const TemplateURL* turl1 =
219      util()->model()->GetTemplateURLForKeyword(ASCIIToUTF16("b"));
220  ASSERT_TRUE(turl1 != NULL);
221  const TemplateURL* turl2 =
222      util()->model()->GetTemplateURLForKeyword(ASCIIToUTF16("b1"));
223  ASSERT_TRUE(turl2 != NULL);
224
225  EXPECT_TRUE(controller()->CanEdit(turl1));
226  EXPECT_TRUE(controller()->CanEdit(turl2));
227
228  // Simulate setting a managed default.  This will add another template URL to
229  // the model.
230  SimulateDefaultSearchIsManaged(turl2->url());
231  EXPECT_TRUE(util()->model()->is_default_search_managed());
232  EXPECT_TRUE(controller()->CanEdit(turl1));
233  EXPECT_TRUE(controller()->CanEdit(turl2));
234  EXPECT_FALSE(
235      controller()->CanEdit(util()->model()->GetDefaultSearchProvider()));
236}
237
238TEST_F(KeywordEditorControllerNoWebDataTest, MakeDefaultNoWebData) {
239  int index = controller()->AddTemplateURL(kA, kB, "http://c{searchTerms}");
240  ClearChangeCount();
241
242  // This should not result in a crash.
243  int new_default = controller()->MakeDefaultTemplateURL(index);
244  EXPECT_EQ(index, new_default);
245}
246
247// Mutates the TemplateURLService and make sure table model is updating
248// appropriately.
249TEST_F(KeywordEditorControllerTest, MutateTemplateURLService) {
250  int original_row_count = table_model()->RowCount();
251
252  TemplateURLData data;
253  data.short_name = ASCIIToUTF16("b");
254  data.SetKeyword(ASCIIToUTF16("a"));
255  TemplateURL* turl = new TemplateURL(data);
256  util()->model()->Add(turl);
257
258  // Table model should have updated.
259  VerifyChangeCount(1, 0, 0, 0);
260
261  // And should contain the newly added TemplateURL.
262  ASSERT_EQ(original_row_count + 1, table_model()->RowCount());
263  ASSERT_GE(table_model()->IndexOfTemplateURL(turl), 0);
264}
265