• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/string_util.h"
6 #include "base/time.h"
7 #include "base/utf_string_conversions.h"
8 #include "build/build_config.h"
9 #include "chrome/browser/autocomplete/autocomplete_match.h"
10 #include "chrome/browser/autocomplete/search_provider.h"
11 #include "chrome/browser/history/history.h"
12 #include "chrome/browser/prefs/pref_service.h"
13 #include "chrome/browser/search_engines/template_url.h"
14 #include "chrome/browser/search_engines/template_url_model.h"
15 #include "chrome/common/net/test_url_fetcher_factory.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/test/testing_browser_process.h"
18 #include "chrome/test/testing_browser_process_test.h"
19 #include "chrome/test/testing_profile.h"
20 #include "content/browser/browser_thread.h"
21 #include "net/url_request/url_request_status.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 
24 // The following environment is configured for these tests:
25 // . The TemplateURL default_t_url_ is set as the default provider.
26 // . The TemplateURL keyword_t_url_ is added to the TemplateURLModel. This
27 //   TemplateURL has a valid suggest and search URL.
28 // . The URL created by using the search term term1_ with default_t_url_ is
29 //   added to history.
30 // . The URL created by using the search term keyword_term_ with keyword_t_url_
31 //   is added to history.
32 // . test_factory_ is set as the URLFetcher::Factory.
33 class SearchProviderTest : public TestingBrowserProcessTest,
34                            public AutocompleteProvider::ACProviderListener {
35  public:
SearchProviderTest()36   SearchProviderTest()
37       : default_t_url_(NULL),
38         term1_(UTF8ToUTF16("term1")),
39         keyword_t_url_(NULL),
40         keyword_term_(UTF8ToUTF16("keyword")),
41         io_thread_(BrowserThread::IO),
42         quit_when_done_(false) {
43     io_thread_.Start();
44   }
45 
46   // See description above class for what this registers.
47   virtual void SetUp();
48 
49   virtual void TearDown();
50 
51  protected:
52   // Returns an AutocompleteMatch in provider_'s set of matches that matches
53   // |url|. If there is no matching URL, an empty match is returned.
54   AutocompleteMatch FindMatchWithDestination(const GURL& url);
55 
56   // ACProviderListener method. If we're waiting for the provider to finish,
57   // this exits the message loop.
58   virtual void OnProviderUpdate(bool updated_matches);
59 
60   // Runs a nested message loop until provider_ is done. The message loop is
61   // exited by way of OnProviderUPdate.
62   void RunTillProviderDone();
63 
64   // Invokes Start on provider_, then runs all pending tasks.
65   void QueryForInput(const string16& text,
66                      bool prevent_inline_autocomplete,
67                      bool minimal_changes);
68 
69   // Notifies the URLFetcher for the suggest query corresponding to the default
70   // search provider that it's done.
71   // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
72   void FinishDefaultSuggestQuery();
73 
74   // See description above class for details of these fields.
75   TemplateURL* default_t_url_;
76   const string16 term1_;
77   GURL term1_url_;
78   TemplateURL* keyword_t_url_;
79   const string16 keyword_term_;
80   GURL keyword_url_;
81 
82   MessageLoopForUI message_loop_;
83   BrowserThread io_thread_;
84 
85   // URLFetcher::Factory implementation registered.
86   TestURLFetcherFactory test_factory_;
87 
88   // Profile we use.
89   TestingProfile profile_;
90 
91   // The provider.
92   scoped_refptr<SearchProvider> provider_;
93 
94   // If true, OnProviderUpdate exits out of the current message loop.
95   bool quit_when_done_;
96 
97   DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
98 };
99 
SetUp()100 void SearchProviderTest::SetUp() {
101   SearchProvider::set_query_suggest_immediately(true);
102 
103   // We need both the history service and template url model loaded.
104   profile_.CreateHistoryService(true, false);
105   profile_.CreateTemplateURLModel();
106 
107   TemplateURLModel* turl_model = profile_.GetTemplateURLModel();
108 
109   // Reset the default TemplateURL.
110   default_t_url_ = new TemplateURL();
111   default_t_url_->SetURL("http://defaultturl/{searchTerms}", 0, 0);
112   default_t_url_->SetSuggestionsURL("http://defaultturl2/{searchTerms}", 0, 0);
113   turl_model->Add(default_t_url_);
114   turl_model->SetDefaultSearchProvider(default_t_url_);
115   TemplateURLID default_provider_id = default_t_url_->id();
116   ASSERT_NE(0, default_provider_id);
117 
118   // Add url1, with search term term1_.
119   HistoryService* history =
120       profile_.GetHistoryService(Profile::EXPLICIT_ACCESS);
121   term1_url_ = GURL(default_t_url_->url()->ReplaceSearchTerms(
122       *default_t_url_, term1_, 0, string16()));
123   history->AddPageWithDetails(term1_url_, string16(), 1, 1,
124                               base::Time::Now(), false,
125                               history::SOURCE_BROWSED);
126   history->SetKeywordSearchTermsForURL(term1_url_, default_t_url_->id(),
127                                        term1_);
128 
129   // Create another TemplateURL.
130   keyword_t_url_ = new TemplateURL();
131   keyword_t_url_->set_keyword(ASCIIToUTF16("k"));
132   keyword_t_url_->SetURL("http://keyword/{searchTerms}", 0, 0);
133   keyword_t_url_->SetSuggestionsURL("http://suggest_keyword/{searchTerms}", 0,
134                                     0);
135   profile_.GetTemplateURLModel()->Add(keyword_t_url_);
136   ASSERT_NE(0, keyword_t_url_->id());
137 
138   // Add a page and search term for keyword_t_url_.
139   keyword_url_ = GURL(keyword_t_url_->url()->ReplaceSearchTerms(
140       *keyword_t_url_, keyword_term_, 0, string16()));
141   history->AddPageWithDetails(keyword_url_, string16(), 1, 1,
142                               base::Time::Now(), false,
143                               history::SOURCE_BROWSED);
144   history->SetKeywordSearchTermsForURL(keyword_url_, keyword_t_url_->id(),
145                                        keyword_term_);
146 
147   // Keywords are updated by the InMemoryHistoryBackend only after the message
148   // has been processed on the history thread. Block until history processes all
149   // requests to ensure the InMemoryDatabase is the state we expect it.
150   profile_.BlockUntilHistoryProcessesPendingRequests();
151 
152   provider_ = new SearchProvider(this, &profile_);
153 
154   URLFetcher::set_factory(&test_factory_);
155 }
156 
OnProviderUpdate(bool updated_matches)157 void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
158   if (quit_when_done_ && provider_->done()) {
159     quit_when_done_ = false;
160     message_loop_.Quit();
161   }
162 }
163 
RunTillProviderDone()164 void SearchProviderTest::RunTillProviderDone() {
165   if (provider_->done())
166     return;
167 
168   quit_when_done_ = true;
169 #if defined(OS_MACOSX)
170   message_loop_.Run();
171 #else
172   message_loop_.Run(NULL);
173 #endif
174 }
175 
QueryForInput(const string16 & text,bool prevent_inline_autocomplete,bool minimal_changes)176 void SearchProviderTest::QueryForInput(const string16& text,
177                                        bool prevent_inline_autocomplete,
178                                        bool minimal_changes) {
179   // Start a query.
180   AutocompleteInput input(text, string16(), prevent_inline_autocomplete,
181                           false, true, AutocompleteInput::ALL_MATCHES);
182   provider_->Start(input, minimal_changes);
183 
184   // RunAllPending so that the task scheduled by SearchProvider to create the
185   // URLFetchers runs.
186   message_loop_.RunAllPending();
187 }
188 
TearDown()189 void SearchProviderTest::TearDown() {
190   message_loop_.RunAllPending();
191 
192   URLFetcher::set_factory(NULL);
193 
194   // Shutdown the provider before the profile.
195   provider_ = NULL;
196 }
197 
FindMatchWithDestination(const GURL & url)198 AutocompleteMatch SearchProviderTest::FindMatchWithDestination(
199     const GURL& url) {
200   for (ACMatches::const_iterator i = provider_->matches().begin();
201        i != provider_->matches().end(); ++i) {
202     if (i->destination_url == url)
203       return *i;
204   }
205   return AutocompleteMatch(NULL, 1, false, AutocompleteMatch::HISTORY_URL);
206 }
207 
FinishDefaultSuggestQuery()208 void SearchProviderTest::FinishDefaultSuggestQuery() {
209   TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
210       SearchProvider::kDefaultProviderURLFetcherID);
211   ASSERT_TRUE(default_fetcher);
212 
213   // Tell the SearchProvider the default suggest query is done.
214   default_fetcher->delegate()->OnURLFetchComplete(
215       default_fetcher, GURL(), net::URLRequestStatus(), 200, ResponseCookies(),
216       std::string());
217 }
218 
219 // Tests -----------------------------------------------------------------------
220 
221 // Make sure we query history for the default provider and a URLFetcher is
222 // created for the default provider suggest results.
TEST_F(SearchProviderTest,QueryDefaultProvider)223 TEST_F(SearchProviderTest, QueryDefaultProvider) {
224   string16 term = term1_.substr(0, term1_.size() - 1);
225   QueryForInput(term, false, false);
226 
227   // Make sure the default providers suggest service was queried.
228   TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
229       SearchProvider::kDefaultProviderURLFetcherID);
230   ASSERT_TRUE(fetcher);
231 
232   // And the URL matches what we expected.
233   GURL expected_url = GURL(default_t_url_->suggestions_url()->
234       ReplaceSearchTerms(*default_t_url_, term, 0, string16()));
235   ASSERT_TRUE(fetcher->original_url() == expected_url);
236 
237   // Tell the SearchProvider the suggest query is done.
238   fetcher->delegate()->OnURLFetchComplete(
239       fetcher, GURL(), net::URLRequestStatus(), 200, ResponseCookies(),
240       std::string());
241   fetcher = NULL;
242 
243   // Run till the history results complete.
244   RunTillProviderDone();
245 
246   // The SearchProvider is done. Make sure it has a result for the history
247   // term term1.
248   AutocompleteMatch term1_match = FindMatchWithDestination(term1_url_);
249   EXPECT_TRUE(!term1_match.destination_url.is_empty());
250   // Term1 should have a description.
251   EXPECT_FALSE(term1_match.description.empty());
252 
253   GURL what_you_typed_url = GURL(default_t_url_->url()->ReplaceSearchTerms(
254       *default_t_url_, term, 0, string16()));
255   AutocompleteMatch what_you_typed_match =
256       FindMatchWithDestination(what_you_typed_url);
257   EXPECT_TRUE(!what_you_typed_match.destination_url.is_empty());
258   EXPECT_TRUE(what_you_typed_match.description.empty());
259 
260   // The match for term1 should be more relevant than the what you typed result.
261   EXPECT_GT(term1_match.relevance, what_you_typed_match.relevance);
262 }
263 
TEST_F(SearchProviderTest,HonorPreventInlineAutocomplete)264 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
265   string16 term = term1_.substr(0, term1_.size() - 1);
266   QueryForInput(term, true, false);
267 
268   ASSERT_FALSE(provider_->matches().empty());
269   ASSERT_EQ(AutocompleteMatch::SEARCH_WHAT_YOU_TYPED,
270             provider_->matches()[0].type);
271 }
272 
273 // Issues a query that matches the registered keyword and makes sure history
274 // is queried as well as URLFetchers getting created.
TEST_F(SearchProviderTest,QueryKeywordProvider)275 TEST_F(SearchProviderTest, QueryKeywordProvider) {
276   string16 term = keyword_term_.substr(0, keyword_term_.size() - 1);
277   QueryForInput(keyword_t_url_->keyword() + UTF8ToUTF16(" ") + term, false,
278                 false);
279 
280   // Make sure the default providers suggest service was queried.
281   TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
282       SearchProvider::kDefaultProviderURLFetcherID);
283   ASSERT_TRUE(default_fetcher);
284 
285   // Tell the SearchProvider the default suggest query is done.
286   default_fetcher->delegate()->OnURLFetchComplete(
287       default_fetcher, GURL(), net::URLRequestStatus(), 200, ResponseCookies(),
288       std::string());
289   default_fetcher = NULL;
290 
291   // Make sure the keyword providers suggest service was queried.
292   TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
293       SearchProvider::kKeywordProviderURLFetcherID);
294   ASSERT_TRUE(keyword_fetcher);
295 
296   // And the URL matches what we expected.
297   GURL expected_url = GURL(keyword_t_url_->suggestions_url()->
298       ReplaceSearchTerms(*keyword_t_url_, term, 0, string16()));
299   ASSERT_TRUE(keyword_fetcher->original_url() == expected_url);
300 
301   // Tell the SearchProvider the keyword suggest query is done.
302   keyword_fetcher->delegate()->OnURLFetchComplete(
303       keyword_fetcher, GURL(), net::URLRequestStatus(), 200, ResponseCookies(),
304       std::string());
305   keyword_fetcher = NULL;
306 
307   // Run till the history results complete.
308   RunTillProviderDone();
309 
310   // The SearchProvider is done. Make sure it has a result for the history
311   // term keyword.
312   AutocompleteMatch match = FindMatchWithDestination(keyword_url_);
313   ASSERT_TRUE(!match.destination_url.is_empty());
314 
315   // The match should have a TemplateURL.
316   EXPECT_TRUE(match.template_url);
317 
318   // The fill into edit should contain the keyword.
319   EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_,
320             match.fill_into_edit);
321 }
322 
TEST_F(SearchProviderTest,DontSendPrivateDataToSuggest)323 TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) {
324   // None of the following input strings should be sent to the suggest server,
325   // because they may contain private data.
326   const char* inputs[] = {
327     "username:password",
328     "http://username:password",
329     "https://username:password",
330     "username:password@hostname",
331     "http://username:password@hostname/",
332     "file://filename",
333     "data://data",
334     "unknownscheme:anything",
335     "http://hostname/?query=q",
336     "http://hostname/path#ref",
337     "https://hostname/path",
338   };
339 
340   for (size_t i = 0; i < arraysize(inputs); ++i) {
341     QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
342     // Make sure the default providers suggest service was not queried.
343     ASSERT_TRUE(test_factory_.GetFetcherByID(
344         SearchProvider::kDefaultProviderURLFetcherID) == NULL);
345     // Run till the history results complete.
346     RunTillProviderDone();
347   }
348 }
349 
350 // Make sure FinalizeInstantQuery works.
TEST_F(SearchProviderTest,FinalizeInstantQuery)351 TEST_F(SearchProviderTest, FinalizeInstantQuery) {
352   PrefService* service = profile_.GetPrefs();
353   service->SetBoolean(prefs::kInstantEnabled, true);
354 
355   QueryForInput(ASCIIToUTF16("foo"), false, false);
356 
357   // Wait until history and the suggest query complete.
358   profile_.BlockUntilHistoryProcessesPendingRequests();
359   ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
360 
361   // When instant is enabled the provider isn't done until it hears from
362   // instant.
363   EXPECT_FALSE(provider_->done());
364 
365   // Tell the provider instant is done.
366   provider_->FinalizeInstantQuery(ASCIIToUTF16("foo"), ASCIIToUTF16("bar"));
367 
368   // The provider should now be done.
369   EXPECT_TRUE(provider_->done());
370 
371   // There should be two matches, one for what you typed, the other for
372   // 'foobar'.
373   EXPECT_EQ(2u, provider_->matches().size());
374   GURL instant_url = GURL(default_t_url_->url()->ReplaceSearchTerms(
375       *default_t_url_, ASCIIToUTF16("foobar"), 0, string16()));
376   AutocompleteMatch instant_match = FindMatchWithDestination(instant_url);
377   EXPECT_TRUE(!instant_match.destination_url.is_empty());
378 
379   // And the 'foobar' match should have a description.
380   EXPECT_FALSE(instant_match.description.empty());
381 
382   // Make sure the what you typed match has no description.
383   GURL what_you_typed_url = GURL(default_t_url_->url()->ReplaceSearchTerms(
384       *default_t_url_, ASCIIToUTF16("foo"), 0, string16()));
385   AutocompleteMatch what_you_typed_match =
386       FindMatchWithDestination(what_you_typed_url);
387   EXPECT_TRUE(!what_you_typed_match.destination_url.is_empty());
388   EXPECT_TRUE(what_you_typed_match.description.empty());
389 
390   // The instant search should be more relevant.
391   EXPECT_GT(instant_match.relevance, what_you_typed_match.relevance);
392 }
393 
394 // Make sure that if FinalizeInstantQuery is invoked before suggest results
395 // return, the suggest text from FinalizeInstantQuery is remembered.
TEST_F(SearchProviderTest,RememberInstantQuery)396 TEST_F(SearchProviderTest, RememberInstantQuery) {
397   PrefService* service = profile_.GetPrefs();
398   service->SetBoolean(prefs::kInstantEnabled, true);
399 
400   QueryForInput(ASCIIToUTF16("foo"), false, false);
401 
402   // Finalize the instant query immediately.
403   provider_->FinalizeInstantQuery(ASCIIToUTF16("foo"), ASCIIToUTF16("bar"));
404 
405   // There should be two matches, one for what you typed, the other for
406   // 'foobar'.
407   EXPECT_EQ(2u, provider_->matches().size());
408   GURL instant_url = GURL(default_t_url_->url()->ReplaceSearchTerms(
409       *default_t_url_, ASCIIToUTF16("foobar"), 0, string16()));
410   AutocompleteMatch instant_match = FindMatchWithDestination(instant_url);
411   EXPECT_FALSE(instant_match.destination_url.is_empty());
412 
413   // Wait until history and the suggest query complete.
414   profile_.BlockUntilHistoryProcessesPendingRequests();
415   ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
416 
417   // Provider should be done.
418   EXPECT_TRUE(provider_->done());
419 
420   // There should be two matches, one for what you typed, the other for
421   // 'foobar'.
422   EXPECT_EQ(2u, provider_->matches().size());
423   instant_match = FindMatchWithDestination(instant_url);
424   EXPECT_FALSE(instant_match.destination_url.is_empty());
425 
426   // And the 'foobar' match should have a description.
427   EXPECT_FALSE(instant_match.description.empty());
428 }
429 
430 // Make sure that if trailing whitespace is added to the text supplied to
431 // AutocompleteInput the default suggest text is cleared.
TEST_F(SearchProviderTest,DifferingText)432 TEST_F(SearchProviderTest, DifferingText) {
433   PrefService* service = profile_.GetPrefs();
434   service->SetBoolean(prefs::kInstantEnabled, true);
435 
436   QueryForInput(ASCIIToUTF16("foo"), false, false);
437 
438   // Wait until history and the suggest query complete.
439   profile_.BlockUntilHistoryProcessesPendingRequests();
440   ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
441 
442   // Finalize the instant query immediately.
443   provider_->FinalizeInstantQuery(ASCIIToUTF16("foo"), ASCIIToUTF16("bar"));
444 
445   // Query with input that ends up getting trimmed to be the same as was
446   // originally supplied.
447   QueryForInput(ASCIIToUTF16("foo "), false, true);
448 
449   // There should only one match, for what you typed.
450   EXPECT_EQ(1u, provider_->matches().size());
451   GURL instant_url = GURL(default_t_url_->url()->ReplaceSearchTerms(
452       *default_t_url_, ASCIIToUTF16("foo"), 0, string16()));
453   AutocompleteMatch instant_match = FindMatchWithDestination(instant_url);
454   EXPECT_FALSE(instant_match.destination_url.is_empty());
455 }
456 
TEST_F(SearchProviderTest,DontAutocompleteURLLikeTerms)457 TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
458   profile_.CreateAutocompleteClassifier();
459   string16 term(ASCIIToUTF16("docs.google.com"));
460   HistoryService* history =
461       profile_.GetHistoryService(Profile::EXPLICIT_ACCESS);
462   GURL url = GURL(default_t_url_->url()->ReplaceSearchTerms(
463       *default_t_url_, term, 0, string16()));
464   history->AddPageWithDetails(
465       url, string16(), 1, 1, base::Time::Now(), false, history::SOURCE_BROWSED);
466   history->SetKeywordSearchTermsForURL(url, default_t_url_->id(), term);
467 
468   // Add the term as a url.
469   history->AddPageWithDetails(
470       GURL("http://docs.google.com"), string16(), 1, 1, base::Time::Now(),
471       false, history::SOURCE_BROWSED);
472 
473   profile_.BlockUntilHistoryProcessesPendingRequests();
474 
475   QueryForInput(ASCIIToUTF16("docs"), false, false);
476 
477   // Wait until history and the suggest query complete.
478   profile_.BlockUntilHistoryProcessesPendingRequests();
479   ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
480 
481   // Provider should be done.
482   EXPECT_TRUE(provider_->done());
483 
484   // There should be two matches, one for what you typed, the other for
485   // 'docs.google.com'. The search term should have a lower priority than the
486   // what you typed match.
487   ASSERT_EQ(2u, provider_->matches().size());
488   AutocompleteMatch term_match = FindMatchWithDestination(url);
489   GURL what_you_typed_url = GURL(default_t_url_->url()->ReplaceSearchTerms(
490       *default_t_url_, ASCIIToUTF16("docs"), 0, string16()));
491   AutocompleteMatch what_you_typed_match =
492       FindMatchWithDestination(what_you_typed_url);
493   EXPECT_GT(what_you_typed_match.relevance, term_match.relevance);
494 }
495