1// Copyright (c) 2012 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 "base/callback_helpers.h"
6#include "base/files/file_util.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/message_loop/message_loop.h"
9#include "base/path_service.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/search_engines/template_url_service_test_util.h"
12#include "chrome/common/chrome_paths.h"
13#include "chrome/test/base/testing_profile.h"
14#include "components/search_engines/template_url.h"
15#include "components/search_engines/template_url_fetcher.h"
16#include "components/search_engines/template_url_service.h"
17#include "content/public/test/test_browser_thread_bundle.h"
18#include "net/test/embedded_test_server/embedded_test_server.h"
19#include "testing/gtest/include/gtest/gtest.h"
20#include "url/gurl.h"
21
22using base::ASCIIToUTF16;
23
24// Basic set-up for TemplateURLFetcher tests.
25class TemplateURLFetcherTest : public testing::Test {
26 public:
27  TemplateURLFetcherTest();
28
29  virtual void SetUp() OVERRIDE {
30    TestingProfile* profile = test_util_.profile();
31    ASSERT_TRUE(profile->GetRequestContext());
32    template_url_fetcher_.reset(new TemplateURLFetcher(
33        test_util_.model(), profile->GetRequestContext()));
34
35    ASSERT_TRUE(test_server_.InitializeAndWaitUntilReady());
36  }
37
38  virtual void TearDown() OVERRIDE {
39    ASSERT_TRUE(test_server_.ShutdownAndWaitUntilComplete());
40  }
41
42  // Called when the callback is destroyed.
43  void DestroyedCallback();
44
45  // TemplateURLFetcherCallbacks implementation.  (Although not derived from
46  // this class, this method handles those calls for the test.)
47  void ConfirmAddSearchProvider(
48      base::ScopedClosureRunner* callback_destruction_notifier,
49      scoped_ptr<TemplateURL> template_url);
50
51  // Schedules the download of the url.
52  void StartDownload(const base::string16& keyword,
53                     const std::string& osdd_file_name,
54                     TemplateURLFetcher::ProviderType provider_type,
55                     bool check_that_file_exists);
56
57  // Waits for any downloads to finish.
58  void WaitForDownloadToFinish();
59
60  TemplateURLServiceTestUtil* test_util() { return &test_util_; }
61  TemplateURLFetcher* template_url_fetcher() {
62    return template_url_fetcher_.get();
63  }
64  const TemplateURL* last_callback_template_url() const {
65    return last_callback_template_url_.get();
66  }
67  int callbacks_destroyed() const { return callbacks_destroyed_; }
68  int add_provider_called() const { return add_provider_called_; }
69
70 private:
71  content::TestBrowserThreadBundle thread_bundle_;  // To set up BrowserThreads.
72  TemplateURLServiceTestUtil test_util_;
73  scoped_ptr<TemplateURLFetcher> template_url_fetcher_;
74  net::test_server::EmbeddedTestServer test_server_;
75
76  // The last TemplateURL to come from a callback.
77  scoped_ptr<TemplateURL> last_callback_template_url_;
78
79  // How many TemplateURLFetcherTestCallbacks have been destructed.
80  int callbacks_destroyed_;
81
82  // How many times ConfirmAddSearchProvider has been called.
83  int add_provider_called_;
84
85  // Is the code in WaitForDownloadToFinish in a message loop waiting for a
86  // callback to finish?
87  bool waiting_for_download_;
88
89 private:
90  DISALLOW_COPY_AND_ASSIGN(TemplateURLFetcherTest);
91};
92
93TemplateURLFetcherTest::TemplateURLFetcherTest()
94    : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
95      callbacks_destroyed_(0),
96      add_provider_called_(0),
97      waiting_for_download_(false) {
98  base::FilePath src_dir;
99  CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
100  test_server_.ServeFilesFromDirectory(
101      src_dir.AppendASCII("chrome/test/data"));
102}
103
104void TemplateURLFetcherTest::DestroyedCallback() {
105  callbacks_destroyed_++;
106  if (waiting_for_download_)
107    base::MessageLoop::current()->Quit();
108}
109
110void TemplateURLFetcherTest::ConfirmAddSearchProvider(
111    base::ScopedClosureRunner* callback_destruction_notifier,
112    scoped_ptr<TemplateURL> template_url) {
113  last_callback_template_url_ = template_url.Pass();
114  add_provider_called_++;
115}
116
117void TemplateURLFetcherTest::StartDownload(
118    const base::string16& keyword,
119    const std::string& osdd_file_name,
120    TemplateURLFetcher::ProviderType provider_type,
121    bool check_that_file_exists) {
122
123  if (check_that_file_exists) {
124    base::FilePath osdd_full_path;
125    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &osdd_full_path));
126    osdd_full_path = osdd_full_path.AppendASCII(osdd_file_name);
127    ASSERT_TRUE(base::PathExists(osdd_full_path));
128    ASSERT_FALSE(base::DirectoryExists(osdd_full_path));
129  }
130
131  // Start the fetch.
132  GURL osdd_url = test_server_.GetURL("/" + osdd_file_name);
133  GURL favicon_url;
134  base::ScopedClosureRunner* callback_destruction_notifier =
135      new base::ScopedClosureRunner(
136          base::Bind(&TemplateURLFetcherTest::DestroyedCallback,
137                     base::Unretained(this)));
138
139  template_url_fetcher_->ScheduleDownload(
140      keyword, osdd_url, favicon_url,
141      TemplateURLFetcher::URLFetcherCustomizeCallback(),
142      base::Bind(&TemplateURLFetcherTest::ConfirmAddSearchProvider,
143                 base::Unretained(this),
144                 base::Owned(callback_destruction_notifier)),
145      provider_type);
146}
147
148void TemplateURLFetcherTest::WaitForDownloadToFinish() {
149  ASSERT_FALSE(waiting_for_download_);
150  waiting_for_download_ = true;
151  base::MessageLoop::current()->Run();
152  waiting_for_download_ = false;
153}
154
155TEST_F(TemplateURLFetcherTest, BasicAutodetectedTest) {
156  base::string16 keyword(ASCIIToUTF16("test"));
157
158  test_util()->ChangeModelToLoadState();
159  ASSERT_FALSE(test_util()->model()->GetTemplateURLForKeyword(keyword));
160
161  std::string osdd_file_name("simple_open_search.xml");
162  StartDownload(keyword, osdd_file_name,
163                TemplateURLFetcher::AUTODETECTED_PROVIDER, true);
164  ASSERT_EQ(0, add_provider_called());
165  ASSERT_EQ(0, callbacks_destroyed());
166
167  WaitForDownloadToFinish();
168  ASSERT_EQ(0, add_provider_called());
169  ASSERT_EQ(1, callbacks_destroyed());
170
171  const TemplateURL* t_url = test_util()->model()->GetTemplateURLForKeyword(
172      keyword);
173  ASSERT_TRUE(t_url);
174  EXPECT_EQ(ASCIIToUTF16("http://example.com/%s/other_stuff"),
175            t_url->url_ref().DisplayURL(
176                test_util()->model()->search_terms_data()));
177  EXPECT_TRUE(t_url->safe_for_autoreplace());
178}
179
180TEST_F(TemplateURLFetcherTest, DuplicatesThrownAway) {
181  base::string16 keyword(ASCIIToUTF16("test"));
182
183  test_util()->ChangeModelToLoadState();
184  ASSERT_FALSE(test_util()->model()->GetTemplateURLForKeyword(keyword));
185
186  std::string osdd_file_name("simple_open_search.xml");
187  StartDownload(keyword, osdd_file_name,
188                TemplateURLFetcher::AUTODETECTED_PROVIDER, true);
189  ASSERT_EQ(0, add_provider_called());
190  ASSERT_EQ(0, callbacks_destroyed());
191
192  struct {
193    std::string description;
194    std::string osdd_file_name;
195    base::string16 keyword;
196    TemplateURLFetcher::ProviderType provider_type;
197  } test_cases[] = {
198      { "Duplicate osdd url with autodetected provider.", osdd_file_name,
199        keyword + ASCIIToUTF16("1"),
200        TemplateURLFetcher::AUTODETECTED_PROVIDER },
201      { "Duplicate keyword with autodetected provider.", osdd_file_name + "1",
202        keyword, TemplateURLFetcher::AUTODETECTED_PROVIDER },
203      { "Duplicate osdd url with explicit provider.", osdd_file_name,
204        base::string16(), TemplateURLFetcher::EXPLICIT_PROVIDER },
205  };
206
207  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
208    StartDownload(test_cases[i].keyword, test_cases[i].osdd_file_name,
209                  test_cases[i].provider_type, false);
210    ASSERT_EQ(1, template_url_fetcher()->requests_count())
211        << test_cases[i].description;
212    ASSERT_EQ(i + 1, static_cast<size_t>(callbacks_destroyed()));
213  }
214
215  WaitForDownloadToFinish();
216  ASSERT_EQ(1 + ARRAYSIZE_UNSAFE(test_cases),
217            static_cast<size_t>(callbacks_destroyed()));
218  ASSERT_EQ(0, add_provider_called());
219}
220
221TEST_F(TemplateURLFetcherTest, BasicExplicitTest) {
222  base::string16 keyword(ASCIIToUTF16("test"));
223
224  test_util()->ChangeModelToLoadState();
225  ASSERT_FALSE(test_util()->model()->GetTemplateURLForKeyword(keyword));
226
227  std::string osdd_file_name("simple_open_search.xml");
228  StartDownload(keyword, osdd_file_name,
229                TemplateURLFetcher::EXPLICIT_PROVIDER, true);
230  ASSERT_EQ(0, add_provider_called());
231  ASSERT_EQ(0, callbacks_destroyed());
232
233  WaitForDownloadToFinish();
234  ASSERT_EQ(1, add_provider_called());
235  ASSERT_EQ(1, callbacks_destroyed());
236
237  ASSERT_TRUE(last_callback_template_url());
238  EXPECT_EQ(ASCIIToUTF16("http://example.com/%s/other_stuff"),
239            last_callback_template_url()->url_ref().DisplayURL(
240                test_util()->model()->search_terms_data()));
241  EXPECT_EQ(ASCIIToUTF16("example.com"),
242            last_callback_template_url()->keyword());
243  EXPECT_FALSE(last_callback_template_url()->safe_for_autoreplace());
244}
245
246TEST_F(TemplateURLFetcherTest, AutodetectedBeforeLoadTest) {
247  base::string16 keyword(ASCIIToUTF16("test"));
248  ASSERT_FALSE(test_util()->model()->GetTemplateURLForKeyword(keyword));
249
250  std::string osdd_file_name("simple_open_search.xml");
251  StartDownload(keyword, osdd_file_name,
252                TemplateURLFetcher::AUTODETECTED_PROVIDER, true);
253  ASSERT_EQ(0, add_provider_called());
254  ASSERT_EQ(1, callbacks_destroyed());
255}
256
257TEST_F(TemplateURLFetcherTest, ExplicitBeforeLoadTest) {
258  base::string16 keyword(ASCIIToUTF16("test"));
259  ASSERT_FALSE(test_util()->model()->GetTemplateURLForKeyword(keyword));
260
261  std::string osdd_file_name("simple_open_search.xml");
262  StartDownload(keyword, osdd_file_name,
263                TemplateURLFetcher::EXPLICIT_PROVIDER, true);
264  ASSERT_EQ(0, add_provider_called());
265  ASSERT_EQ(0, callbacks_destroyed());
266
267  WaitForDownloadToFinish();
268  ASSERT_EQ(1, add_provider_called());
269  ASSERT_EQ(1, callbacks_destroyed());
270
271  ASSERT_TRUE(last_callback_template_url());
272  EXPECT_EQ(ASCIIToUTF16("http://example.com/%s/other_stuff"),
273            last_callback_template_url()->url_ref().DisplayURL(
274                test_util()->model()->search_terms_data()));
275  EXPECT_EQ(ASCIIToUTF16("example.com"),
276            last_callback_template_url()->keyword());
277  EXPECT_FALSE(last_callback_template_url()->safe_for_autoreplace());
278}
279
280TEST_F(TemplateURLFetcherTest, DuplicateKeywordsTest) {
281  base::string16 keyword(ASCIIToUTF16("test"));
282  TemplateURLData data;
283  data.short_name = keyword;
284  data.SetKeyword(keyword);
285  data.SetURL("http://example.com/");
286  test_util()->model()->Add(new TemplateURL(data));
287  test_util()->ChangeModelToLoadState();
288
289  ASSERT_TRUE(test_util()->model()->GetTemplateURLForKeyword(keyword));
290
291  // This should bail because the keyword already exists.
292  std::string osdd_file_name("simple_open_search.xml");
293  StartDownload(keyword, osdd_file_name,
294                TemplateURLFetcher::AUTODETECTED_PROVIDER, true);
295  ASSERT_EQ(0, add_provider_called());
296  ASSERT_EQ(1, callbacks_destroyed());
297  ASSERT_FALSE(last_callback_template_url());
298}
299
300TEST_F(TemplateURLFetcherTest, DuplicateDownloadTest) {
301  base::string16 keyword(ASCIIToUTF16("test"));
302  std::string osdd_file_name("simple_open_search.xml");
303  StartDownload(keyword, osdd_file_name,
304                TemplateURLFetcher::EXPLICIT_PROVIDER, true);
305  ASSERT_EQ(0, add_provider_called());
306  ASSERT_EQ(0, callbacks_destroyed());
307
308  // This should bail because the keyword already has a pending download.
309  StartDownload(keyword, osdd_file_name,
310                TemplateURLFetcher::EXPLICIT_PROVIDER, true);
311  ASSERT_EQ(0, add_provider_called());
312  ASSERT_EQ(1, callbacks_destroyed());
313
314  WaitForDownloadToFinish();
315  ASSERT_EQ(1, add_provider_called());
316  ASSERT_EQ(2, callbacks_destroyed());
317  ASSERT_TRUE(last_callback_template_url());
318}
319