search_provider_unittest.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
1// Copyright 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 "chrome/browser/autocomplete/search_provider.h"
6
7#include <string>
8
9#include "base/command_line.h"
10#include "base/metrics/field_trial.h"
11#include "base/prefs/pref_service.h"
12#include "base/run_loop.h"
13#include "base/strings/string16.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/string_util.h"
16#include "base/strings/utf_string_conversions.h"
17#include "base/time/time.h"
18#include "build/build_config.h"
19#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
20#include "chrome/browser/autocomplete/autocomplete_controller.h"
21#include "chrome/browser/autocomplete/autocomplete_input.h"
22#include "chrome/browser/autocomplete/autocomplete_match.h"
23#include "chrome/browser/autocomplete/autocomplete_provider.h"
24#include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
25#include "chrome/browser/autocomplete/history_url_provider.h"
26#include "chrome/browser/history/history_service.h"
27#include "chrome/browser/history/history_service_factory.h"
28#include "chrome/browser/omnibox/omnibox_field_trial.h"
29#include "chrome/browser/search_engines/template_url.h"
30#include "chrome/browser/search_engines/template_url_service.h"
31#include "chrome/browser/search_engines/template_url_service_factory.h"
32#include "chrome/browser/signin/signin_manager_factory.h"
33#include "chrome/browser/sync/profile_sync_service.h"
34#include "chrome/browser/sync/profile_sync_service_factory.h"
35#include "chrome/common/chrome_switches.h"
36#include "chrome/common/pref_names.h"
37#include "chrome/test/base/testing_browser_process.h"
38#include "chrome/test/base/testing_profile.h"
39#include "components/google/core/browser/google_switches.h"
40#include "components/metrics/proto/omnibox_event.pb.h"
41#include "components/search_engines/search_engine_type.h"
42#include "components/signin/core/browser/signin_manager.h"
43#include "components/sync_driver/pref_names.h"
44#include "components/variations/entropy_provider.h"
45#include "components/variations/variations_associated_data.h"
46#include "content/public/test/test_browser_thread_bundle.h"
47#include "net/url_request/test_url_fetcher_factory.h"
48#include "net/url_request/url_request_status.h"
49#include "testing/gtest/include/gtest/gtest.h"
50
51using base::ASCIIToUTF16;
52
53namespace {
54
55// Returns the first match in |matches| with |allowed_to_be_default_match|
56// set to true.
57ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) {
58  ACMatches::const_iterator it = matches.begin();
59  while ((it != matches.end()) && !it->allowed_to_be_default_match)
60    ++it;
61  return it;
62}
63
64class SuggestionDeletionHandler;
65class SearchProviderForTest : public SearchProvider {
66 public:
67  SearchProviderForTest(
68      AutocompleteProviderListener* listener,
69      Profile* profile);
70  bool is_success() { return is_success_; };
71
72 protected:
73  virtual ~SearchProviderForTest();
74
75 private:
76  virtual void RecordDeletionResult(bool success) OVERRIDE;
77  bool is_success_;
78  DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest);
79};
80
81SearchProviderForTest::SearchProviderForTest(
82    AutocompleteProviderListener* listener,
83    Profile* profile)
84    : SearchProvider(listener, profile), is_success_(false) {
85}
86
87SearchProviderForTest::~SearchProviderForTest() {
88}
89
90void SearchProviderForTest::RecordDeletionResult(bool success) {
91  is_success_ = success;
92}
93
94} // namespace
95
96// SearchProviderTest ---------------------------------------------------------
97
98// The following environment is configured for these tests:
99// . The TemplateURL default_t_url_ is set as the default provider.
100// . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This
101//   TemplateURL has a valid suggest and search URL.
102// . The URL created by using the search term term1_ with default_t_url_ is
103//   added to history.
104// . The URL created by using the search term keyword_term_ with keyword_t_url_
105//   is added to history.
106// . test_factory_ is set as the URLFetcherFactory.
107class SearchProviderTest : public testing::Test,
108                           public AutocompleteProviderListener {
109 public:
110  struct ResultInfo {
111    ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES),
112                   allowed_to_be_default_match(false) {
113    }
114    ResultInfo(GURL gurl,
115               AutocompleteMatch::Type result_type,
116               bool allowed_to_be_default_match,
117               base::string16 fill_into_edit)
118      : gurl(gurl),
119        result_type(result_type),
120        allowed_to_be_default_match(allowed_to_be_default_match),
121        fill_into_edit(fill_into_edit) {
122    }
123
124    const GURL gurl;
125    const AutocompleteMatch::Type result_type;
126    const bool allowed_to_be_default_match;
127    const base::string16 fill_into_edit;
128  };
129
130  struct TestData {
131    const base::string16 input;
132    const size_t num_results;
133    const ResultInfo output[3];
134  };
135
136  SearchProviderTest()
137      : default_t_url_(NULL),
138        term1_(ASCIIToUTF16("term1")),
139        keyword_t_url_(NULL),
140        keyword_term_(ASCIIToUTF16("keyword")),
141        run_loop_(NULL) {
142    ResetFieldTrialList();
143  }
144
145  // See description above class for what this registers.
146  virtual void SetUp() OVERRIDE;
147  virtual void TearDown() OVERRIDE;
148
149  void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
150
151 protected:
152  // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
153  scoped_ptr<base::FieldTrialList> field_trial_list_;
154
155  // Default value used for testing.
156  static const std::string kNotApplicable;
157
158  // Adds a search for |term|, using the engine |t_url| to the history, and
159  // returns the URL for that search.
160  GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count);
161
162  // Looks for a match in |provider_| with |contents| equal to |contents|.
163  // Sets |match| to it if found.  Returns whether |match| was set.
164  bool FindMatchWithContents(const base::string16& contents,
165                             AutocompleteMatch* match);
166
167  // Looks for a match in |provider_| with destination |url|.  Sets |match| to
168  // it if found.  Returns whether |match| was set.
169  bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match);
170
171  // AutocompleteProviderListener:
172  // If we're waiting for the provider to finish, this exits the message loop.
173  virtual void OnProviderUpdate(bool updated_matches) OVERRIDE;
174
175  // Runs a nested message loop until provider_ is done. The message loop is
176  // exited by way of OnProviderUpdate.
177  void RunTillProviderDone();
178
179  // Invokes Start on provider_, then runs all pending tasks.
180  void QueryForInput(const base::string16& text,
181                     bool prevent_inline_autocomplete,
182                     bool prefer_keyword);
183
184  // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
185  // non-NULL, sets it to the "what you typed" entry for |text|.
186  void QueryForInputAndSetWYTMatch(const base::string16& text,
187                                   AutocompleteMatch* wyt_match);
188
189  // Notifies the URLFetcher for the suggest query corresponding to the default
190  // search provider that it's done.
191  // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
192  void FinishDefaultSuggestQuery();
193
194  // Runs SearchProvider on |input|, for which the suggest server replies
195  // with |json|, and expects that the resulting matches' contents equals
196  // that in |matches|.  An empty entry in |matches| means no match should
197  // be returned in that position.  Reports any errors with a message that
198  // includes |error_description|.
199  void ForcedQueryTestHelper(const std::string& input,
200                             const std::string& json,
201                             const std::string matches[3],
202                             const std::string& error_description);
203
204  void ResetFieldTrialList();
205
206  void ClearAllResults();
207
208  // See description above class for details of these fields.
209  TemplateURL* default_t_url_;
210  const base::string16 term1_;
211  GURL term1_url_;
212  TemplateURL* keyword_t_url_;
213  const base::string16 keyword_term_;
214  GURL keyword_url_;
215
216  content::TestBrowserThreadBundle thread_bundle_;
217
218  // URLFetcherFactory implementation registered.
219  net::TestURLFetcherFactory test_factory_;
220
221  // Profile we use.
222  TestingProfile profile_;
223
224  // The provider.
225  scoped_refptr<SearchProviderForTest> provider_;
226
227  // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
228  base::RunLoop* run_loop_;
229
230  DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
231};
232
233// static
234const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
235
236void SearchProviderTest::SetUp() {
237  // Make sure that fetchers are automatically ungregistered upon destruction.
238  test_factory_.set_remove_fetcher_on_delete(true);
239
240  // We need both the history service and template url model loaded.
241  ASSERT_TRUE(profile_.CreateHistoryService(true, false));
242  TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
243      &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
244
245  TemplateURLService* turl_model =
246      TemplateURLServiceFactory::GetForProfile(&profile_);
247
248  turl_model->Load();
249
250  // Reset the default TemplateURL.
251  TemplateURLData data;
252  data.short_name = ASCIIToUTF16("t");
253  data.SetURL("http://defaultturl/{searchTerms}");
254  data.suggestions_url = "http://defaultturl2/{searchTerms}";
255  data.instant_url = "http://does/not/exist?strk=1";
256  data.search_terms_replacement_key = "strk";
257  default_t_url_ = new TemplateURL(data);
258  turl_model->Add(default_t_url_);
259  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
260  TemplateURLID default_provider_id = default_t_url_->id();
261  ASSERT_NE(0, default_provider_id);
262
263  // Add url1, with search term term1_.
264  term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
265
266  // Create another TemplateURL.
267  data.short_name = ASCIIToUTF16("k");
268  data.SetKeyword(ASCIIToUTF16("k"));
269  data.SetURL("http://keyword/{searchTerms}");
270  data.suggestions_url = "http://suggest_keyword/{searchTerms}";
271  keyword_t_url_ = new TemplateURL(data);
272  turl_model->Add(keyword_t_url_);
273  ASSERT_NE(0, keyword_t_url_->id());
274
275  // Add a page and search term for keyword_t_url_.
276  keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
277
278  // Keywords are updated by the InMemoryHistoryBackend only after the message
279  // has been processed on the history thread. Block until history processes all
280  // requests to ensure the InMemoryDatabase is the state we expect it.
281  profile_.BlockUntilHistoryProcessesPendingRequests();
282
283  provider_ = new SearchProviderForTest(this, &profile_);
284  provider_->kMinimumTimeBetweenSuggestQueriesMs = 0;
285}
286
287void SearchProviderTest::TearDown() {
288  base::RunLoop().RunUntilIdle();
289
290  // Shutdown the provider before the profile.
291  provider_ = NULL;
292}
293
294void SearchProviderTest::RunTest(TestData* cases,
295                                 int num_cases,
296                                 bool prefer_keyword) {
297  ACMatches matches;
298  for (int i = 0; i < num_cases; ++i) {
299    AutocompleteInput input(cases[i].input, base::string16::npos,
300                            base::string16(), GURL(),
301                            metrics::OmniboxEventProto::INVALID_SPEC, false,
302                            prefer_keyword, true, true);
303    provider_->Start(input, false);
304    matches = provider_->matches();
305    base::string16 diagnostic_details =
306        ASCIIToUTF16("Input was: ") +
307        cases[i].input +
308        ASCIIToUTF16("; prefer_keyword was: ") +
309        (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false"));
310    EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details;
311    if (matches.size() == cases[i].num_results) {
312      for (size_t j = 0; j < cases[i].num_results; ++j) {
313        EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) <<
314            diagnostic_details;
315        EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) <<
316            diagnostic_details;
317        EXPECT_EQ(cases[i].output[j].fill_into_edit,
318                  matches[j].fill_into_edit) << diagnostic_details;
319        EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
320                  matches[j].allowed_to_be_default_match) << diagnostic_details;
321      }
322    }
323  }
324}
325
326void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
327  if (run_loop_ && provider_->done()) {
328    run_loop_->Quit();
329    run_loop_ = NULL;
330  }
331}
332
333void SearchProviderTest::RunTillProviderDone() {
334  if (provider_->done())
335    return;
336
337  base::RunLoop run_loop;
338  run_loop_ = &run_loop;
339  run_loop.Run();
340}
341
342void SearchProviderTest::QueryForInput(const base::string16& text,
343                                       bool prevent_inline_autocomplete,
344                                       bool prefer_keyword) {
345  // Start a query.
346  AutocompleteInput input(text, base::string16::npos, base::string16(), GURL(),
347                          metrics::OmniboxEventProto::INVALID_SPEC,
348                          prevent_inline_autocomplete, prefer_keyword, true,
349                          true);
350  provider_->Start(input, false);
351
352  // RunUntilIdle so that the task scheduled by SearchProvider to create the
353  // URLFetchers runs.
354  base::RunLoop().RunUntilIdle();
355}
356
357void SearchProviderTest::QueryForInputAndSetWYTMatch(
358    const base::string16& text,
359    AutocompleteMatch* wyt_match) {
360  QueryForInput(text, false, false);
361  profile_.BlockUntilHistoryProcessesPendingRequests();
362  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
363  if (!wyt_match)
364    return;
365  ASSERT_GE(provider_->matches().size(), 1u);
366  EXPECT_TRUE(FindMatchWithDestination(
367      GURL(default_t_url_->url_ref().ReplaceSearchTerms(
368          TemplateURLRef::SearchTermsArgs(base::CollapseWhitespace(
369              text, false)),
370          TemplateURLServiceFactory::GetForProfile(
371              &profile_)->search_terms_data())),
372      wyt_match));
373}
374
375GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
376                                            base::string16 term,
377                                            int visit_count) {
378  HistoryService* history =
379      HistoryServiceFactory::GetForProfile(&profile_,
380                                           Profile::EXPLICIT_ACCESS);
381  GURL search(t_url->url_ref().ReplaceSearchTerms(
382      TemplateURLRef::SearchTermsArgs(term),
383      TemplateURLServiceFactory::GetForProfile(
384          &profile_)->search_terms_data()));
385  static base::Time last_added_time;
386  last_added_time = std::max(base::Time::Now(),
387      last_added_time + base::TimeDelta::FromMicroseconds(1));
388  history->AddPageWithDetails(search, base::string16(), visit_count, visit_count,
389      last_added_time, false, history::SOURCE_BROWSED);
390  history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
391  return search;
392}
393
394bool SearchProviderTest::FindMatchWithContents(const base::string16& contents,
395                                               AutocompleteMatch* match) {
396  for (ACMatches::const_iterator i = provider_->matches().begin();
397       i != provider_->matches().end(); ++i) {
398    if (i->contents == contents) {
399      *match = *i;
400      return true;
401    }
402  }
403  return false;
404}
405
406bool SearchProviderTest::FindMatchWithDestination(const GURL& url,
407                                                  AutocompleteMatch* match) {
408  for (ACMatches::const_iterator i = provider_->matches().begin();
409       i != provider_->matches().end(); ++i) {
410    if (i->destination_url == url) {
411      *match = *i;
412      return true;
413    }
414  }
415  return false;
416}
417
418void SearchProviderTest::FinishDefaultSuggestQuery() {
419  net::TestURLFetcher* default_fetcher =
420      test_factory_.GetFetcherByID(
421          SearchProvider::kDefaultProviderURLFetcherID);
422  ASSERT_TRUE(default_fetcher);
423
424  // Tell the SearchProvider the default suggest query is done.
425  default_fetcher->set_response_code(200);
426  default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
427}
428
429void SearchProviderTest::ForcedQueryTestHelper(
430    const std::string& input,
431    const std::string& json,
432    const std::string expected_matches[3],
433    const std::string& error_description) {
434  QueryForInput(ASCIIToUTF16(input), false, false);
435  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
436      SearchProvider::kDefaultProviderURLFetcherID);
437  ASSERT_TRUE(fetcher);
438  fetcher->set_response_code(200);
439  fetcher->SetResponseString(json);
440  fetcher->delegate()->OnURLFetchComplete(fetcher);
441  RunTillProviderDone();
442
443  const ACMatches& matches = provider_->matches();
444  ASSERT_LE(matches.size(), 3u);
445  size_t i = 0;
446  // Ensure that the returned matches equal the expectations.
447  for (; i < matches.size(); ++i) {
448    EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) <<
449        error_description;
450  }
451  // Ensure that no expected matches are missing.
452  for (; i < 3u; ++i) {
453    EXPECT_EQ(std::string(), expected_matches[i]) <<
454        "Case #" << i << ": " << error_description;
455  }
456}
457
458void SearchProviderTest::ResetFieldTrialList() {
459  // Destroy the existing FieldTrialList before creating a new one to avoid
460  // a DCHECK.
461  field_trial_list_.reset();
462  field_trial_list_.reset(new base::FieldTrialList(
463      new metrics::SHA1EntropyProvider("foo")));
464  chrome_variations::testing::ClearAllVariationParams();
465  base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
466      "AutocompleteDynamicTrial_0", "DefaultGroup");
467  trial->group();
468}
469
470void SearchProviderTest::ClearAllResults() {
471  provider_->ClearAllResults();
472}
473
474// Actual Tests ---------------------------------------------------------------
475
476// Make sure we query history for the default provider and a URLFetcher is
477// created for the default provider suggest results.
478TEST_F(SearchProviderTest, QueryDefaultProvider) {
479  base::string16 term = term1_.substr(0, term1_.length() - 1);
480  QueryForInput(term, false, false);
481
482  // Make sure the default providers suggest service was queried.
483  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
484      SearchProvider::kDefaultProviderURLFetcherID);
485  ASSERT_TRUE(fetcher);
486
487  // And the URL matches what we expected.
488  GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
489      TemplateURLRef::SearchTermsArgs(term),
490      TemplateURLServiceFactory::GetForProfile(
491          &profile_)->search_terms_data()));
492  ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
493
494  // Tell the SearchProvider the suggest query is done.
495  fetcher->set_response_code(200);
496  fetcher->delegate()->OnURLFetchComplete(fetcher);
497  fetcher = NULL;
498
499  // Run till the history results complete.
500  RunTillProviderDone();
501
502  // The SearchProvider is done. Make sure it has a result for the history
503  // term term1.
504  AutocompleteMatch term1_match;
505  EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match));
506  // Term1 should not have a description, it's set later.
507  EXPECT_TRUE(term1_match.description.empty());
508
509  AutocompleteMatch wyt_match;
510  EXPECT_TRUE(FindMatchWithDestination(
511      GURL(default_t_url_->url_ref().ReplaceSearchTerms(
512          TemplateURLRef::SearchTermsArgs(term),
513          TemplateURLServiceFactory::GetForProfile(
514              &profile_)->search_terms_data())),
515      &wyt_match));
516  EXPECT_TRUE(wyt_match.description.empty());
517
518  // The match for term1 should be more relevant than the what you typed match.
519  EXPECT_GT(term1_match.relevance, wyt_match.relevance);
520  // This longer match should be inlineable.
521  EXPECT_TRUE(term1_match.allowed_to_be_default_match);
522  // The what you typed match should be too, of course.
523  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
524}
525
526TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
527  base::string16 term = term1_.substr(0, term1_.length() - 1);
528  QueryForInput(term, true, false);
529
530  ASSERT_FALSE(provider_->matches().empty());
531  ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
532            provider_->matches()[0].type);
533  EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match);
534}
535
536// Issues a query that matches the registered keyword and makes sure history
537// is queried as well as URLFetchers getting created.
538TEST_F(SearchProviderTest, QueryKeywordProvider) {
539  base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
540  QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
541                false,
542                false);
543
544  // Make sure the default providers suggest service was queried.
545  net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
546      SearchProvider::kDefaultProviderURLFetcherID);
547  ASSERT_TRUE(default_fetcher);
548
549  // Tell the SearchProvider the default suggest query is done.
550  default_fetcher->set_response_code(200);
551  default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
552  default_fetcher = NULL;
553
554  // Make sure the keyword providers suggest service was queried.
555  net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
556      SearchProvider::kKeywordProviderURLFetcherID);
557  ASSERT_TRUE(keyword_fetcher);
558
559  // And the URL matches what we expected.
560  GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
561      TemplateURLRef::SearchTermsArgs(term),
562      TemplateURLServiceFactory::GetForProfile(
563          &profile_)->search_terms_data()));
564  ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
565
566  // Tell the SearchProvider the keyword suggest query is done.
567  keyword_fetcher->set_response_code(200);
568  keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
569  keyword_fetcher = NULL;
570
571  // Run till the history results complete.
572  RunTillProviderDone();
573
574  // The SearchProvider is done. Make sure it has a result for the history
575  // term keyword.
576  AutocompleteMatch match;
577  EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match));
578
579  // The match should have an associated keyword.
580  EXPECT_FALSE(match.keyword.empty());
581
582  // The fill into edit should contain the keyword.
583  EXPECT_EQ(keyword_t_url_->keyword() + base::char16(' ') + keyword_term_,
584            match.fill_into_edit);
585}
586
587TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) {
588  // None of the following input strings should be sent to the suggest server,
589  // because they may contain private data.
590  const char* inputs[] = {
591    "username:password",
592    "http://username:password",
593    "https://username:password",
594    "username:password@hostname",
595    "http://username:password@hostname/",
596    "file://filename",
597    "data://data",
598    "unknownscheme:anything",
599    "http://hostname/?query=q",
600    "http://hostname/path#ref",
601    "http://hostname/path #ref",
602    "https://hostname/path",
603  };
604
605  for (size_t i = 0; i < arraysize(inputs); ++i) {
606    QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
607    // Make sure the default provider's suggest service was not queried.
608    ASSERT_TRUE(test_factory_.GetFetcherByID(
609        SearchProvider::kDefaultProviderURLFetcherID) == NULL);
610    // Run till the history results complete.
611    RunTillProviderDone();
612  }
613}
614
615TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) {
616  // All of the following input strings should be sent to the suggest server,
617  // because they should not get caught by the private data checks.
618  const char* inputs[] = {
619    "query",
620    "query with spaces",
621    "http://hostname",
622    "http://hostname/path",
623    "http://hostname #ref",
624    "www.hostname.com #ref",
625    "https://hostname",
626    "#hashtag",
627    "foo https://hostname/path"
628  };
629
630  profile_.BlockUntilHistoryProcessesPendingRequests();
631  for (size_t i = 0; i < arraysize(inputs); ++i) {
632    QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
633    // Make sure the default provider's suggest service was queried.
634    ASSERT_TRUE(test_factory_.GetFetcherByID(
635        SearchProvider::kDefaultProviderURLFetcherID) != NULL);
636  }
637}
638
639TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
640  AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
641      &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
642  GURL url = AddSearchToHistory(default_t_url_,
643                                ASCIIToUTF16("docs.google.com"), 1);
644
645  // Add the term as a url.
646  HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)->
647      AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1, 1,
648                         base::Time::Now(), false, history::SOURCE_BROWSED);
649  profile_.BlockUntilHistoryProcessesPendingRequests();
650
651  AutocompleteMatch wyt_match;
652  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
653                                                      &wyt_match));
654
655  // There should be two matches, one for what you typed, the other for
656  // 'docs.google.com'. The search term should have a lower priority than the
657  // what you typed match.
658  ASSERT_EQ(2u, provider_->matches().size());
659  AutocompleteMatch term_match;
660  EXPECT_TRUE(FindMatchWithDestination(url, &term_match));
661  EXPECT_GT(wyt_match.relevance, term_match.relevance);
662  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
663  EXPECT_TRUE(term_match.allowed_to_be_default_match);
664}
665
666TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) {
667  const std::string kEmptyMatch;
668  struct {
669    const std::string json;
670    const std::string matches_in_default_mode[3];
671    const std::string matches_in_forced_query_mode[3];
672  } cases[] = {
673    // Without suggested relevance scores.
674    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
675       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]",
676      { "a", "a1.com", "a2" },
677      { "a", "a2", kEmptyMatch } },
678
679    // With suggested relevance scores in a situation where navsuggest would
680    // go second.
681    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
682       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
683        "\"google:suggestrelevance\":[1250, 1200]}]",
684      { "a", "a1.com", "a2" },
685      { "a", "a2", kEmptyMatch } },
686
687    // With suggested relevance scores in a situation where navsuggest
688    // would go first.
689    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
690       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
691        "\"google:suggestrelevance\":[1350, 1250]}]",
692      { "a1.com", "a", "a2" },
693      { "a", "a2", kEmptyMatch } },
694
695    // With suggested relevance scores in a situation where navsuggest
696    // would go first only because verbatim has been demoted.
697    { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
698       "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
699        "\"google:suggestrelevance\":[1450, 1400],"
700        "\"google:verbatimrelevance\":1350}]",
701      { "a1.com", "a2", "a" },
702      { "a2", "a", kEmptyMatch } },
703  };
704
705  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
706    ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode,
707                           "regular input with json=" + cases[i].json);
708    ForcedQueryTestHelper("?a", cases[i].json,
709                          cases[i].matches_in_forced_query_mode,
710                          "forced query input with json=" + cases[i].json);
711  }
712}
713
714// A multiword search with one visit should not autocomplete until multiple
715// words are typed.
716TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
717  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
718                                   1));
719  profile_.BlockUntilHistoryProcessesPendingRequests();
720
721  AutocompleteMatch wyt_match;
722  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"),
723                                                      &wyt_match));
724  ASSERT_EQ(2u, provider_->matches().size());
725  AutocompleteMatch term_match;
726  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
727  EXPECT_GT(wyt_match.relevance, term_match.relevance);
728  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
729  EXPECT_TRUE(term_match.allowed_to_be_default_match);
730
731  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"),
732                                                      &wyt_match));
733  ASSERT_EQ(2u, provider_->matches().size());
734  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
735  EXPECT_GT(term_match.relevance, wyt_match.relevance);
736  EXPECT_TRUE(term_match.allowed_to_be_default_match);
737  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
738}
739
740// A multiword search with more than one visit should autocomplete immediately.
741TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
742  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
743                                   2));
744  profile_.BlockUntilHistoryProcessesPendingRequests();
745
746  AutocompleteMatch wyt_match;
747  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"),
748                                                      &wyt_match));
749  ASSERT_EQ(2u, provider_->matches().size());
750  AutocompleteMatch term_match;
751  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
752  EXPECT_GT(term_match.relevance, wyt_match.relevance);
753  EXPECT_TRUE(term_match.allowed_to_be_default_match);
754  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
755}
756
757// Autocompletion should work at a word boundary after a space, and should
758// offer a suggestion for the trimmed search query.
759TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
760  AddSearchToHistory(default_t_url_, ASCIIToUTF16("two  searches "), 2);
761  GURL suggested_url(default_t_url_->url_ref().ReplaceSearchTerms(
762      TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("two searches")),
763      TemplateURLServiceFactory::GetForProfile(
764          &profile_)->search_terms_data()));
765  profile_.BlockUntilHistoryProcessesPendingRequests();
766
767  AutocompleteMatch wyt_match;
768  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "),
769                                                      &wyt_match));
770  ASSERT_EQ(2u, provider_->matches().size());
771  AutocompleteMatch term_match;
772  EXPECT_TRUE(FindMatchWithDestination(suggested_url, &term_match));
773  EXPECT_GT(term_match.relevance, wyt_match.relevance);
774  EXPECT_TRUE(term_match.allowed_to_be_default_match);
775  EXPECT_EQ(ASCIIToUTF16("searches"), term_match.inline_autocompletion);
776  EXPECT_EQ(ASCIIToUTF16("two searches"), term_match.fill_into_edit);
777  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
778}
779
780// Newer multiword searches should score more highly than older ones.
781TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
782  GURL term_url_a(AddSearchToHistory(default_t_url_,
783                                     ASCIIToUTF16("three searches aaa"), 1));
784  GURL term_url_b(AddSearchToHistory(default_t_url_,
785                                     ASCIIToUTF16("three searches bbb"), 1));
786  profile_.BlockUntilHistoryProcessesPendingRequests();
787
788  AutocompleteMatch wyt_match;
789  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"),
790                                                      &wyt_match));
791  ASSERT_EQ(3u, provider_->matches().size());
792  AutocompleteMatch term_match_a;
793  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
794  AutocompleteMatch term_match_b;
795  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
796  EXPECT_GT(term_match_b.relevance, term_match_a.relevance);
797  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
798  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
799  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
800  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
801}
802
803// An autocompleted multiword search should not be replaced by a different
804// autocompletion while the user is still typing a valid prefix.
805TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
806  GURL term_url_a(AddSearchToHistory(default_t_url_,
807                                     ASCIIToUTF16("four searches aaa"), 2));
808  GURL term_url_b(AddSearchToHistory(default_t_url_,
809                                     ASCIIToUTF16("four searches bbb"), 1));
810  profile_.BlockUntilHistoryProcessesPendingRequests();
811
812  AutocompleteMatch wyt_match;
813  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
814                                                      &wyt_match));
815  ASSERT_EQ(3u, provider_->matches().size());
816  AutocompleteMatch term_match_a;
817  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
818  AutocompleteMatch term_match_b;
819  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
820  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
821  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
822  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
823  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
824  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
825
826  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
827                                                      &wyt_match));
828  ASSERT_EQ(3u, provider_->matches().size());
829  EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
830  EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
831  EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
832  EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
833  EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
834  EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
835  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
836}
837
838// Non-completable multiword searches should not crowd out single-word searches.
839TEST_F(SearchProviderTest, DontCrowdOutSingleWords) {
840  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1));
841  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1);
842  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1);
843  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1);
844  AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1);
845  profile_.BlockUntilHistoryProcessesPendingRequests();
846
847  AutocompleteMatch wyt_match;
848  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"),
849                                                      &wyt_match));
850  ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size());
851  AutocompleteMatch term_match;
852  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
853  EXPECT_GT(term_match.relevance, wyt_match.relevance);
854  EXPECT_TRUE(term_match.allowed_to_be_default_match);
855  EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
856}
857
858// Inline autocomplete matches regardless of case differences from the input.
859TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
860  GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
861  profile_.BlockUntilHistoryProcessesPendingRequests();
862
863  AutocompleteMatch wyt_match;
864  ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
865                                                      &wyt_match));
866  ASSERT_EQ(2u, provider_->matches().size());
867  AutocompleteMatch term_match;
868  EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
869  EXPECT_GT(term_match.relevance, wyt_match.relevance);
870  EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit);
871  EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion);
872  EXPECT_TRUE(term_match.allowed_to_be_default_match);
873}
874
875// Verifies AutocompleteControllers return results (including keyword
876// results) in the right order and set descriptions for them correctly.
877TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
878  // Add an entry that corresponds to a keyword search with 'term2'.
879  AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
880  profile_.BlockUntilHistoryProcessesPendingRequests();
881
882  AutocompleteController controller(&profile_, NULL,
883      AutocompleteProvider::TYPE_SEARCH);
884  controller.Start(AutocompleteInput(
885      ASCIIToUTF16("k t"), base::string16::npos, base::string16(), GURL(),
886      metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, true));
887  const AutocompleteResult& result = controller.result();
888
889  // There should be three matches, one for the keyword history, one for
890  // keyword provider's what-you-typed, and one for the default provider's
891  // what you typed, in that order.
892  ASSERT_EQ(3u, result.size());
893  EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type);
894  EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
895            result.match_at(1).type);
896  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
897            result.match_at(2).type);
898  EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance);
899  EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
900  EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
901  EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
902  EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match);
903
904  // The two keyword results should come with the keyword we expect.
905  EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
906  EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword);
907  // The default provider has a different keyword.  (We don't explicitly
908  // set it during this test, so all we do is assert that it's different.)
909  EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword);
910
911  // The top result will always have a description.  The third result,
912  // coming from a different provider than the first two, should also.
913  // Whether the second result has one doesn't matter much.  (If it was
914  // missing, people would infer that it's the same search provider as
915  // the one above it.)
916  EXPECT_FALSE(result.match_at(0).description.empty());
917  EXPECT_FALSE(result.match_at(2).description.empty());
918  EXPECT_NE(result.match_at(0).description, result.match_at(2).description);
919}
920
921TEST_F(SearchProviderTest, KeywordVerbatim) {
922  TestData cases[] = {
923    // Test a simple keyword input.
924    { ASCIIToUTF16("k foo"), 2,
925      { ResultInfo(GURL("http://keyword/foo"),
926                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
927                   true,
928                   ASCIIToUTF16("k foo")),
929        ResultInfo(GURL("http://defaultturl/k%20foo"),
930                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
931                   false,
932                   ASCIIToUTF16("k foo") ) } },
933
934    // Make sure extra whitespace after the keyword doesn't change the
935    // keyword verbatim query.  Also verify that interior consecutive
936    // whitespace gets trimmed.
937    { ASCIIToUTF16("k   foo"), 2,
938      { ResultInfo(GURL("http://keyword/foo"),
939                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
940                   true,
941                   ASCIIToUTF16("k foo")),
942        ResultInfo(GURL("http://defaultturl/k%20foo"),
943                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
944                   false,
945                   ASCIIToUTF16("k foo")) } },
946    // Leading whitespace should be stripped before SearchProvider gets the
947    // input; hence there are no tests here about how it handles those inputs.
948
949    // Verify that interior consecutive whitespace gets trimmed in either case.
950    { ASCIIToUTF16("k  foo  bar"), 2,
951      { ResultInfo(GURL("http://keyword/foo%20bar"),
952                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
953                   true,
954                   ASCIIToUTF16("k foo bar")),
955        ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
956                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
957                   false,
958                   ASCIIToUTF16("k foo bar")) } },
959
960    // Verify that trailing whitespace gets trimmed.
961    { ASCIIToUTF16("k foo bar  "), 2,
962      { ResultInfo(GURL("http://keyword/foo%20bar"),
963                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
964                   true,
965                   ASCIIToUTF16("k foo bar")),
966        ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
967                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
968                   false,
969                   ASCIIToUTF16("k foo bar")) } },
970
971    // Keywords can be prefixed by certain things that should get ignored
972    // when constructing the keyword match.
973    { ASCIIToUTF16("www.k foo"), 2,
974      { ResultInfo(GURL("http://keyword/foo"),
975                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
976                   true,
977                   ASCIIToUTF16("k foo")),
978        ResultInfo(GURL("http://defaultturl/www.k%20foo"),
979                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
980                   false,
981                   ASCIIToUTF16("www.k foo")) } },
982    { ASCIIToUTF16("http://k foo"), 2,
983      { ResultInfo(GURL("http://keyword/foo"),
984                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
985                   true,
986                   ASCIIToUTF16("k foo")),
987        ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
988                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
989                   false,
990                   ASCIIToUTF16("http://k foo")) } },
991    { ASCIIToUTF16("http://www.k foo"), 2,
992      { ResultInfo(GURL("http://keyword/foo"),
993                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
994                   true,
995                   ASCIIToUTF16("k foo")),
996        ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
997                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
998                   false,
999                   ASCIIToUTF16("http://www.k foo")) } },
1000
1001    // A keyword with no remaining input shouldn't get a keyword
1002    // verbatim match.
1003    { ASCIIToUTF16("k"), 1,
1004      { ResultInfo(GURL("http://defaultturl/k"),
1005                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1006                   true,
1007                   ASCIIToUTF16("k")) } },
1008    // Ditto.  Trailing whitespace shouldn't make a difference.
1009    { ASCIIToUTF16("k "), 1,
1010      { ResultInfo(GURL("http://defaultturl/k"),
1011                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1012                   true,
1013                   ASCIIToUTF16("k")) } }
1014
1015    // The fact that verbatim queries to keyword are handled by KeywordProvider
1016    // not SearchProvider is tested in
1017    // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc.
1018  };
1019
1020  // Test not in keyword mode.
1021  RunTest(cases, arraysize(cases), false);
1022
1023  // Test in keyword mode.  (Both modes should give the same result.)
1024  RunTest(cases, arraysize(cases), true);
1025}
1026
1027// Ensures command-line flags are reflected in the URLs the search provider
1028// generates.
1029TEST_F(SearchProviderTest, CommandLineOverrides) {
1030  TemplateURLService* turl_model =
1031      TemplateURLServiceFactory::GetForProfile(&profile_);
1032
1033  TemplateURLData data;
1034  data.short_name = ASCIIToUTF16("default");
1035  data.SetKeyword(data.short_name);
1036  data.SetURL("{google:baseURL}{searchTerms}");
1037  default_t_url_ = new TemplateURL(data);
1038  turl_model->Add(default_t_url_);
1039  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
1040
1041  CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL,
1042                                                      "http://www.bar.com/");
1043  CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1044      switches::kExtraSearchQueryParams, "a=b");
1045
1046  TestData cases[] = {
1047    { ASCIIToUTF16("k a"), 2,
1048      { ResultInfo(GURL("http://keyword/a"),
1049                   AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1050                   true,
1051                   ASCIIToUTF16("k a")),
1052        ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
1053                   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1054                   false,
1055                   ASCIIToUTF16("k a")) } },
1056  };
1057
1058  RunTest(cases, arraysize(cases), false);
1059}
1060
1061// Verifies Navsuggest results don't set a TemplateURL, which Instant relies on.
1062// Also verifies that just the *first* navigational result is listed as a match
1063// if suggested relevance scores were not sent.
1064TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) {
1065  QueryForInput(ASCIIToUTF16("a.c"), false, false);
1066
1067  // Make sure the default providers suggest service was queried.
1068  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
1069      SearchProvider::kDefaultProviderURLFetcherID);
1070  ASSERT_TRUE(fetcher);
1071
1072  // Tell the SearchProvider the suggest query is done.
1073  fetcher->set_response_code(200);
1074  fetcher->SetResponseString(
1075      "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[],"
1076      "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]");
1077  fetcher->delegate()->OnURLFetchComplete(fetcher);
1078  fetcher = NULL;
1079
1080  // Run till the history results complete.
1081  RunTillProviderDone();
1082
1083  // Make sure the only match is 'a.com' and it doesn't have a template_url.
1084  AutocompleteMatch nav_match;
1085  EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match));
1086  EXPECT_TRUE(nav_match.keyword.empty());
1087  EXPECT_TRUE(nav_match.allowed_to_be_default_match);
1088  EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match));
1089}
1090
1091// Verifies that the most relevant suggest results are added properly.
1092TEST_F(SearchProviderTest, SuggestRelevance) {
1093  QueryForInput(ASCIIToUTF16("a"), false, false);
1094
1095  // Make sure the default provider's suggest service was queried.
1096  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
1097      SearchProvider::kDefaultProviderURLFetcherID);
1098  ASSERT_TRUE(fetcher);
1099
1100  // Tell the SearchProvider the suggest query is done.
1101  fetcher->set_response_code(200);
1102  fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]");
1103  fetcher->delegate()->OnURLFetchComplete(fetcher);
1104  fetcher = NULL;
1105
1106  // Run till the history results complete.
1107  RunTillProviderDone();
1108
1109  // Check the expected verbatim and (first 3) suggestions' relative relevances.
1110  AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4;
1111  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
1112  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1));
1113  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2));
1114  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3));
1115  EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4));
1116  EXPECT_GT(verbatim.relevance, match_a1.relevance);
1117  EXPECT_GT(match_a1.relevance, match_a2.relevance);
1118  EXPECT_GT(match_a2.relevance, match_a3.relevance);
1119  EXPECT_TRUE(verbatim.allowed_to_be_default_match);
1120  EXPECT_TRUE(match_a1.allowed_to_be_default_match);
1121  EXPECT_TRUE(match_a2.allowed_to_be_default_match);
1122  EXPECT_TRUE(match_a3.allowed_to_be_default_match);
1123}
1124
1125// Verifies that the default provider abandons suggested relevance scores
1126// when in keyword mode.  This should happen regardless of whether the
1127// keyword provider returns suggested relevance scores.
1128TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) {
1129  struct {
1130    const std::string default_provider_json;
1131    const std::string keyword_provider_json;
1132    const std::string matches[5];
1133  } cases[] = {
1134    // First, try an input where the keyword provider does not deliver
1135    // suggested relevance scores.
1136    { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1137      "{\"google:verbatimrelevance\":9700,"
1138      "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1139      "\"google:suggestrelevance\":[9900, 9800]}]",
1140      "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]",
1141      { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } },
1142
1143    // Now try with keyword provider suggested relevance scores.
1144    { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1145      "{\"google:verbatimrelevance\":9700,"
1146      "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1147      "\"google:suggestrelevance\":[9900, 9800]}]",
1148      "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"],"
1149      "\"google:verbatimrelevance\":9500,"
1150      "\"google:suggestrelevance\":[9600]}]",
1151      { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } }
1152  };
1153
1154  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1155    QueryForInput(ASCIIToUTF16("k a"), false, true);
1156    net::TestURLFetcher* default_fetcher =
1157        test_factory_.GetFetcherByID(
1158            SearchProvider::kDefaultProviderURLFetcherID);
1159    ASSERT_TRUE(default_fetcher);
1160    default_fetcher->set_response_code(200);
1161    default_fetcher->SetResponseString(cases[i].default_provider_json);
1162    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1163    net::TestURLFetcher* keyword_fetcher =
1164        test_factory_.GetFetcherByID(
1165            SearchProvider::kKeywordProviderURLFetcherID);
1166    ASSERT_TRUE(keyword_fetcher);
1167    keyword_fetcher->set_response_code(200);
1168    keyword_fetcher->SetResponseString(cases[i].keyword_provider_json);
1169    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1170    RunTillProviderDone();
1171
1172    const std::string description = "for input with default_provider_json=" +
1173        cases[i].default_provider_json + " and keyword_provider_json=" +
1174        cases[i].keyword_provider_json;
1175    const ACMatches& matches = provider_->matches();
1176    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1177    size_t j = 0;
1178    // Ensure that the returned matches equal the expectations.
1179    for (; j < matches.size(); ++j) {
1180      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents) <<
1181          description;
1182    }
1183    // Ensure that no expected matches are missing.
1184    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1185      EXPECT_EQ(std::string(), cases[i].matches[j]) << description;
1186  }
1187}
1188
1189// Verifies that suggest results with relevance scores are added
1190// properly when using the default fetcher.  When adding a new test
1191// case to this test, please consider adding it to the tests in
1192// KeywordFetcherSuggestRelevance below.
1193TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
1194  struct DefaultFetcherMatch {
1195    std::string contents;
1196    bool allowed_to_be_default_match;
1197  };
1198  const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1199  struct {
1200    const std::string json;
1201    const DefaultFetcherMatch matches[6];
1202    const std::string inline_autocompletion;
1203  } cases[] = {
1204    // Ensure that suggestrelevance scores reorder matches.
1205    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1206      { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch, kEmptyMatch,
1207        kEmptyMatch },
1208      std::string() },
1209    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1210       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1211        "\"google:suggestrelevance\":[1, 2]}]",
1212      { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch,
1213        kEmptyMatch, kEmptyMatch },
1214      std::string() },
1215
1216    // Without suggested relevance scores, we should only allow one
1217    // navsuggest result to be be displayed.
1218    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1219       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1220      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1221        kEmptyMatch, kEmptyMatch },
1222      std::string() },
1223
1224    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1225    // Negative values will have no effect; the calculated value will be used.
1226    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1227                             "\"google:suggestrelevance\":[9998]}]",
1228      { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1229        kEmptyMatch },
1230      std::string() },
1231    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1232                             "\"google:suggestrelevance\":[9999]}]",
1233      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1234        kEmptyMatch },
1235      "1" },
1236    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1237                             "\"google:suggestrelevance\":[9999]}]",
1238      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1239        kEmptyMatch },
1240      "1" },
1241    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1242                             "\"google:suggestrelevance\":[9999]}]",
1243      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1244        kEmptyMatch },
1245      "1" },
1246    { "[\"a\",[\"http://a.com\"],[],[],"
1247       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1248        "\"google:verbatimrelevance\":9999,"
1249        "\"google:suggestrelevance\":[9998]}]",
1250      { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1251        kEmptyMatch },
1252      std::string() },
1253    { "[\"a\",[\"http://a.com\"],[],[],"
1254       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1255        "\"google:verbatimrelevance\":9998,"
1256        "\"google:suggestrelevance\":[9999]}]",
1257      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1258        kEmptyMatch },
1259      ".com" },
1260    { "[\"a\",[\"http://a.com\"],[],[],"
1261       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1262        "\"google:verbatimrelevance\":0,"
1263        "\"google:suggestrelevance\":[9999]}]",
1264      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1265        kEmptyMatch },
1266      ".com" },
1267    { "[\"a\",[\"http://a.com\"],[],[],"
1268       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1269        "\"google:verbatimrelevance\":-1,"
1270        "\"google:suggestrelevance\":[9999]}]",
1271      { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1272        kEmptyMatch },
1273      ".com" },
1274
1275    // Ensure that both types of relevance scores reorder matches together.
1276    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1277                                     "\"google:verbatimrelevance\":9998}]",
1278      { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1279        kEmptyMatch },
1280      "1" },
1281
1282    // Allow non-inlineable matches to be the highest-scoring match but,
1283    // if the result set lacks a single inlineable result, abandon suggested
1284    // relevance scores entirely.
1285    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1286      { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1287        kEmptyMatch },
1288      std::string() },
1289    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1290                            "\"google:verbatimrelevance\":0}]",
1291      { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1292        kEmptyMatch },
1293      std::string() },
1294    { "[\"a\",[\"http://b.com\"],[],[],"
1295       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1296        "\"google:suggestrelevance\":[9999]}]",
1297      { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch,
1298        kEmptyMatch, kEmptyMatch },
1299      std::string() },
1300    { "[\"a\",[\"http://b.com\"],[],[],"
1301       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1302        "\"google:suggestrelevance\":[9999],"
1303        "\"google:verbatimrelevance\":0}]",
1304      { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch,
1305        kEmptyMatch, kEmptyMatch },
1306      std::string() },
1307
1308    // Allow low-scoring matches.
1309    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1310      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1311        kEmptyMatch },
1312      "1" },
1313    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1314      { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1315        kEmptyMatch },
1316      "1" },
1317    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1318                             "\"google:verbatimrelevance\":0}]",
1319      { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1320        kEmptyMatch },
1321      "1" },
1322    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1323                                     "\"google:verbatimrelevance\":0}]",
1324      { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1325        kEmptyMatch },
1326      "2" },
1327    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1328      "\"google:verbatimrelevance\":2}]",
1329      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1330        kEmptyMatch },
1331      "2" },
1332    { "[\"a\",[\"http://a.com\"],[],[],"
1333       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1334        "\"google:suggestrelevance\":[1],"
1335        "\"google:verbatimrelevance\":0}]",
1336      { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1337        kEmptyMatch },
1338      ".com" },
1339    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1340       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1341        "\"google:suggestrelevance\":[1, 2],"
1342        "\"google:verbatimrelevance\":0}]",
1343      { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1344        kEmptyMatch, kEmptyMatch },
1345      "2.com" },
1346
1347    // Ensure that all suggestions are considered, regardless of order.
1348    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1349       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1350      { { "a", true }, { "h", false }, { "g", false }, { "f", false },
1351        { "e", false }, { "d", false } },
1352      std::string() },
1353    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1354              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1355              "\"http://h.com\"],[],[],"
1356       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1357                                "\"NAVIGATION\", \"NAVIGATION\","
1358                                "\"NAVIGATION\", \"NAVIGATION\","
1359                                "\"NAVIGATION\"],"
1360        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1361      { { "a", true }, { "h.com", false }, { "g.com", false },
1362        { "f.com", false }, { "e.com", false }, { "d.com", false } },
1363      std::string() },
1364
1365    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1366    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1367      { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch, kEmptyMatch,
1368        kEmptyMatch },
1369      std::string() },
1370    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1371      { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1372        kEmptyMatch },
1373      std::string() },
1374    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1375       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1376        "\"google:suggestrelevance\":[1]}]",
1377      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1378        kEmptyMatch, kEmptyMatch },
1379      std::string() },
1380    { "[\"a\",[\"http://a1.com\"],[],[],"
1381       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1382       "\"google:suggestrelevance\":[9999, 1]}]",
1383      { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch,
1384        kEmptyMatch, kEmptyMatch },
1385      std::string() },
1386
1387    // Ensure that all 'verbatim' results are merged with their maximum score.
1388    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1389       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1390      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1391        kEmptyMatch },
1392      "2" },
1393    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1394       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1395        "\"google:verbatimrelevance\":0}]",
1396      { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch,
1397        kEmptyMatch },
1398      "2" },
1399
1400    // Ensure that verbatim is always generated without other suggestions.
1401    // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1402    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1403      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1404        kEmptyMatch },
1405      std::string() },
1406    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1407      { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch,
1408        kEmptyMatch },
1409      std::string() },
1410  };
1411
1412  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1413    QueryForInput(ASCIIToUTF16("a"), false, false);
1414    net::TestURLFetcher* fetcher =
1415        test_factory_.GetFetcherByID(
1416            SearchProvider::kDefaultProviderURLFetcherID);
1417    ASSERT_TRUE(fetcher);
1418    fetcher->set_response_code(200);
1419    fetcher->SetResponseString(cases[i].json);
1420    fetcher->delegate()->OnURLFetchComplete(fetcher);
1421    RunTillProviderDone();
1422
1423    const std::string description = "for input with json=" + cases[i].json;
1424    const ACMatches& matches = provider_->matches();
1425    ASSERT_FALSE(matches.empty());
1426    // Find the first match that's allowed to be the default match and check
1427    // its inline_autocompletion.
1428    ACMatches::const_iterator it = FindDefaultMatch(matches);
1429    ASSERT_NE(matches.end(), it);
1430    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1431              it->inline_autocompletion) << description;
1432
1433    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1434    size_t j = 0;
1435    // Ensure that the returned matches equal the expectations.
1436    for (; j < matches.size(); ++j) {
1437      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1438                matches[j].contents) << description;
1439      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1440                matches[j].allowed_to_be_default_match) << description;
1441    }
1442    // Ensure that no expected matches are missing.
1443    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1444      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1445          "Case # " << i << " " << description;
1446  }
1447}
1448
1449// Verifies that suggest results with relevance scores are added
1450// properly when using the keyword fetcher.  This is similar to the
1451// test DefaultFetcherSuggestRelevance above but this uses inputs that
1452// trigger keyword suggestions (i.e., "k a" rather than "a") and has
1453// different expectations (because now the results are a mix of
1454// keyword suggestions and default provider suggestions).  When a new
1455// test is added to this TEST_F, please consider if it would be
1456// appropriate to add to DefaultFetcherSuggestRelevance as well.
1457TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1458  struct KeywordFetcherMatch {
1459    std::string contents;
1460    bool from_keyword;
1461    bool allowed_to_be_default_match;
1462  };
1463  const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1464  struct {
1465    const std::string json;
1466    const KeywordFetcherMatch matches[6];
1467    const std::string inline_autocompletion;
1468  } cases[] = {
1469    // Ensure that suggest relevance scores reorder matches and that
1470    // the keyword verbatim (lacking a suggested verbatim score) beats
1471    // the default provider verbatim.
1472    { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1473      { { "a",   true,  true },
1474        { "k a", false, false },
1475        { "c",   true,  false },
1476        { "b",   true,  false },
1477        kEmptyMatch, kEmptyMatch },
1478      std::string() },
1479    // Again, check that relevance scores reorder matches, just this
1480    // time with navigation matches.  This also checks that with
1481    // suggested relevance scores we allow multiple navsuggest results.
1482    // Note that navsuggest results that come from a keyword provider
1483    // are marked as not a keyword result.  (They don't go to a
1484    // keyword search engine.)
1485    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1486       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1487       "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1488      { { "a",     true,  true },
1489        { "d",     true,  false },
1490        { "c.com", false, false },
1491        { "b.com", false, false },
1492        { "k a",   false, false },
1493        kEmptyMatch },
1494      std::string() },
1495
1496    // Without suggested relevance scores, we should only allow one
1497    // navsuggest result to be be displayed.
1498    { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1499       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1500      { { "a",     true,  true },
1501        { "b.com", false, false },
1502        { "k a",   false, false },
1503        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1504      std::string() },
1505
1506    // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1507    // Negative values will have no effect; the calculated value will be used.
1508    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1509                             "\"google:suggestrelevance\":[9998]}]",
1510      { { "a",   true,  true },
1511        { "a1",  true,  true },
1512        { "k a", false, false },
1513        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1514      std::string() },
1515    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1516                             "\"google:suggestrelevance\":[9999]}]",
1517      { { "a1",  true,  true },
1518        { "a",   true,  true },
1519        { "k a", false, false },
1520        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1521      "1" },
1522    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1523                             "\"google:suggestrelevance\":[9999]}]",
1524      { { "a1",  true,  true },
1525        { "k a", false, false },
1526        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1527      "1" },
1528    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1529                             "\"google:suggestrelevance\":[9999]}]",
1530      { { "a1",  true,  true },
1531        { "a",   true,  true },
1532        { "k a", false, false },
1533        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1534      "1" },
1535    { "[\"a\",[\"http://a.com\"],[],[],"
1536       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1537        "\"google:verbatimrelevance\":9999,"
1538        "\"google:suggestrelevance\":[9998]}]",
1539      { { "a",     true,  true },
1540        { "a.com", false, false },
1541        { "k a",   false, false },
1542        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1543      std::string() },
1544
1545    // Ensure that both types of relevance scores reorder matches together.
1546    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1547                                     "\"google:verbatimrelevance\":9998}]",
1548      { { "a1",  true,  true },
1549        { "a",   true,  true },
1550        { "a2",  true,  true },
1551        { "k a", false, false },
1552        kEmptyMatch, kEmptyMatch },
1553      "1" },
1554
1555    // Check that non-inlinable matches may be ranked as the highest result
1556    // if there is at least one inlineable match.
1557    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1558      { { "b",   true,  false },
1559        { "a",   true,  true },
1560        { "k a", false, false },
1561        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1562      std::string() },
1563    { "[\"a\",[\"http://b.com\"],[],[],"
1564       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1565        "\"google:suggestrelevance\":[9999]}]",
1566      { { "b.com", false, false },
1567        { "a",     true,  true },
1568        { "k a",   false, false },
1569        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1570      std::string() },
1571    // On the other hand, if there is no inlineable match, restore
1572    // the keyword verbatim score.
1573    { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1574                            "\"google:verbatimrelevance\":0}]",
1575      { { "b",   true,  false },
1576        { "a",   true,  true },
1577        { "k a", false, false },
1578        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1579      std::string() },
1580    { "[\"a\",[\"http://b.com\"],[],[],"
1581       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1582        "\"google:suggestrelevance\":[9999],"
1583        "\"google:verbatimrelevance\":0}]",
1584      { { "b.com", false, false },
1585        { "a",     true,  true },
1586        { "k a",   false, false },
1587        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1588      std::string() },
1589
1590    // The top result does not have to score as highly as calculated
1591    // verbatim.  i.e., there are no minimum score restrictions in
1592    // this provider.
1593    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1594      { { "a1",  true,  true },
1595        { "k a", false, false },
1596        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1597      "1" },
1598    { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1599      { { "a1",  true,  true },
1600        { "k a", false, false },
1601        { "a",   true,  true },
1602        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1603      "1" },
1604    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1605                             "\"google:verbatimrelevance\":0}]",
1606      { { "k a", false, false },
1607        { "a1",   true, true },
1608        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1609      "1" },
1610    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1611                                     "\"google:verbatimrelevance\":0}]",
1612      {
1613        { "k a", false, false },
1614        { "a2",  true,  true },
1615        { "a1",  true,  true },
1616        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1617      "2" },
1618    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1619      "\"google:verbatimrelevance\":2}]",
1620      { { "k a", false, false },
1621        { "a2",  true,  true },
1622        { "a",   true,  true },
1623        { "a1",  true,  true },
1624        kEmptyMatch, kEmptyMatch },
1625      "2" },
1626
1627    // Ensure that all suggestions are considered, regardless of order.
1628    { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1629       "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1630      { { "a",   true,  true },
1631        { "k a", false, false },
1632        { "h",   true,  false },
1633        { "g",   true,  false },
1634        { "f",   true,  false },
1635        { "e",   true,  false } },
1636      std::string() },
1637    { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1638              "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1639              "\"http://h.com\"],[],[],"
1640       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1641                                "\"NAVIGATION\", \"NAVIGATION\","
1642                                "\"NAVIGATION\", \"NAVIGATION\","
1643                                "\"NAVIGATION\"],"
1644        "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1645      { { "a",     true,  true },
1646        { "k a",   false, false },
1647        { "h.com", false, false },
1648        { "g.com", false, false },
1649        { "f.com", false, false },
1650        { "e.com", false, false } },
1651      std::string() },
1652
1653    // Ensure that incorrectly sized suggestion relevance lists are ignored.
1654    // Note that keyword suggestions by default (not in suggested relevance
1655    // mode) score more highly than the default verbatim.
1656    { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1657      { { "a",   true,  true },
1658        { "a1",  true,  true },
1659        { "a2",  true,  true },
1660        { "k a", false, false },
1661        kEmptyMatch, kEmptyMatch },
1662      std::string() },
1663    { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1664      { { "a",   true,  true },
1665        { "a1",  true,  true },
1666        { "k a", false, false },
1667        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1668      std::string() },
1669    // In this case, ignoring the suggested relevance scores means we keep
1670    // only one navsuggest result.
1671    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1672       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1673        "\"google:suggestrelevance\":[1]}]",
1674      { { "a",      true,  true },
1675        { "a1.com", false, false },
1676        { "k a",    false, false },
1677        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1678      std::string() },
1679    { "[\"a\",[\"http://a1.com\"],[],[],"
1680       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1681       "\"google:suggestrelevance\":[9999, 1]}]",
1682      { { "a",      true,  true },
1683        { "a1.com", false, false },
1684        { "k a",    false, false },
1685        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1686      std::string() },
1687
1688    // Ensure that all 'verbatim' results are merged with their maximum score.
1689    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1690       "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1691      { { "a2",  true,  true },
1692        { "a",   true,  true },
1693        { "a1",  true,  true },
1694        { "k a", false, false },
1695        kEmptyMatch, kEmptyMatch },
1696      "2" },
1697    { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1698       "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1699        "\"google:verbatimrelevance\":0}]",
1700      { { "a2",  true,  true },
1701        { "a",   true,  true },
1702        { "a1",  true,  true },
1703        { "k a", false, false },
1704        kEmptyMatch, kEmptyMatch },
1705      "2" },
1706
1707    // Ensure that verbatim is always generated without other suggestions.
1708    // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1709    // (except when suggested relevances are ignored).
1710    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1711      { { "k a", false, false },
1712        { "a",   true,  true },
1713        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1714      std::string() },
1715    { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1716      { { "a",   true,  true },
1717        { "k a", false, false },
1718        kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1719      std::string() },
1720
1721    // In reorder mode, navsuggestions will not need to be demoted (because
1722    // they are marked as not allowed to be default match and will be
1723    // reordered as necessary).
1724    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1725       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1726        "\"google:verbatimrelevance\":9990,"
1727        "\"google:suggestrelevance\":[9998, 9999]}]",
1728      { { "a2.com", false, false },
1729        { "a1.com", false, false },
1730        { "a",      true,  true },
1731        { "k a",    false, false },
1732        kEmptyMatch, kEmptyMatch },
1733      std::string() },
1734    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1735       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1736        "\"google:verbatimrelevance\":9990,"
1737        "\"google:suggestrelevance\":[9999, 9998]}]",
1738      { { "a1.com", false, false },
1739        { "a2.com", false, false },
1740        { "a",      true,  true },
1741        { "k a",    false, false },
1742        kEmptyMatch, kEmptyMatch },
1743      std::string() },
1744    { "[\"a\",[\"https://a/\"],[],[],"
1745       "{\"google:suggesttype\":[\"NAVIGATION\"],"
1746        "\"google:suggestrelevance\":[9999]}]",
1747      { { "https://a", false, false },
1748        { "a",         true,  true },
1749        { "k a",       false, false },
1750        kEmptyMatch, kEmptyMatch, kEmptyMatch },
1751      std::string() },
1752    // Check when navsuggest scores more than verbatim and there is query
1753    // suggestion but it scores lower.
1754    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1755       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1756        "\"google:verbatimrelevance\":9990,"
1757        "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
1758      { { "a2.com", false, false },
1759        { "a1.com", false, false },
1760        { "a",      true,  true },
1761        { "a3",     true,  true },
1762        { "k a",    false, false },
1763        kEmptyMatch },
1764      std::string() },
1765    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1766       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1767        "\"google:verbatimrelevance\":9990,"
1768        "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
1769      { { "a1.com", false, false },
1770        { "a2.com", false, false },
1771        { "a",      true,  true },
1772        { "a3",     true,  true },
1773        { "k a",    false, false },
1774        kEmptyMatch },
1775      std::string() },
1776    // Check when navsuggest scores more than a query suggestion.  There is
1777    // a verbatim but it scores lower.
1778    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1779       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1780        "\"google:verbatimrelevance\":9990,"
1781        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1782      { { "a2.com", false, false },
1783        { "a1.com", false, false },
1784        { "a3",     true,  true },
1785        { "a",      true,  true },
1786        { "k a",    false, false },
1787        kEmptyMatch },
1788      "3" },
1789    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1790       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1791        "\"google:verbatimrelevance\":9990,"
1792        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1793      { { "a1.com", false, false },
1794        { "a2.com", false, false },
1795        { "a3",     true,  true },
1796        { "a",      true,  true },
1797        { "k a",    false, false },
1798        kEmptyMatch },
1799      "3" },
1800    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1801       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1802        "\"google:verbatimrelevance\":0,"
1803        "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1804      { { "a2.com", false, false },
1805        { "a1.com", false, false },
1806        { "a3",     true,  true },
1807        { "k a",    false, false },
1808        kEmptyMatch, kEmptyMatch },
1809      "3" },
1810    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1811       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1812        "\"google:verbatimrelevance\":0,"
1813        "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1814      { { "a1.com", false, false },
1815        { "a2.com", false, false },
1816        { "a3",     true,  true },
1817        { "k a",    false, false },
1818        kEmptyMatch, kEmptyMatch },
1819      "3" },
1820    // Check when there is neither verbatim nor a query suggestion that,
1821    // because we can't demote navsuggestions below a query suggestion,
1822    // we restore the keyword verbatim score.
1823    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1824       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1825        "\"google:verbatimrelevance\":0,"
1826        "\"google:suggestrelevance\":[9998, 9999]}]",
1827      { { "a2.com", false, false },
1828        { "a1.com", false, false },
1829        { "a",      true,  true },
1830        { "k a",    false, false },
1831        kEmptyMatch, kEmptyMatch },
1832      std::string() },
1833    { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1834       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1835        "\"google:verbatimrelevance\":0,"
1836        "\"google:suggestrelevance\":[9999, 9998]}]",
1837      { { "a1.com", false, false },
1838        { "a2.com", false, false },
1839        { "a",      true,  true },
1840        { "k a",    false, false },
1841        kEmptyMatch, kEmptyMatch },
1842      std::string() },
1843    // More checks that everything works when it's not necessary to demote.
1844    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1845       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1846        "\"google:verbatimrelevance\":9990,"
1847        "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
1848      { { "a3",     true,  true },
1849        { "a2.com", false, false },
1850        { "a1.com", false, false },
1851        { "a",      true,  true },
1852        { "k a",    false, false },
1853        kEmptyMatch },
1854      "3" },
1855    { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1856       "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1857        "\"google:verbatimrelevance\":9990,"
1858        "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1859      { { "a3",     true,  true },
1860        { "a1.com", false, false },
1861        { "a2.com", false, false },
1862        { "a",      true,  true },
1863        { "k a",    false, false },
1864        kEmptyMatch },
1865      "3" },
1866  };
1867
1868  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1869    QueryForInput(ASCIIToUTF16("k a"), false, true);
1870
1871    // Set up a default fetcher with no results.
1872    net::TestURLFetcher* default_fetcher =
1873        test_factory_.GetFetcherByID(
1874            SearchProvider::kDefaultProviderURLFetcherID);
1875    ASSERT_TRUE(default_fetcher);
1876    default_fetcher->set_response_code(200);
1877    default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1878    default_fetcher = NULL;
1879
1880    // Set up a keyword fetcher with provided results.
1881    net::TestURLFetcher* keyword_fetcher =
1882        test_factory_.GetFetcherByID(
1883            SearchProvider::kKeywordProviderURLFetcherID);
1884    ASSERT_TRUE(keyword_fetcher);
1885    keyword_fetcher->set_response_code(200);
1886    keyword_fetcher->SetResponseString(cases[i].json);
1887    keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1888    keyword_fetcher = NULL;
1889    RunTillProviderDone();
1890
1891    const std::string description = "for input with json=" + cases[i].json;
1892    const ACMatches& matches = provider_->matches();
1893    ASSERT_FALSE(matches.empty());
1894    // Find the first match that's allowed to be the default match and check
1895    // its inline_autocompletion.
1896    ACMatches::const_iterator it = FindDefaultMatch(matches);
1897    ASSERT_NE(matches.end(), it);
1898    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1899              it->inline_autocompletion) << description;
1900
1901    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1902    size_t j = 0;
1903    // Ensure that the returned matches equal the expectations.
1904    for (; j < matches.size(); ++j) {
1905      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1906                matches[j].contents) << description;
1907      EXPECT_EQ(cases[i].matches[j].from_keyword,
1908                matches[j].keyword == ASCIIToUTF16("k")) << description;
1909      EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1910                matches[j].allowed_to_be_default_match) << description;
1911    }
1912    // Ensure that no expected matches are missing.
1913    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1914      EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1915          "Case # " << i << " " << description;
1916  }
1917}
1918
1919TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
1920  // We hardcode the string "term1" below, so ensure that the search term that
1921  // got added to history already is that string.
1922  ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
1923  base::string16 term = term1_.substr(0, term1_.length() - 1);
1924
1925  AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
1926  profile_.BlockUntilHistoryProcessesPendingRequests();
1927
1928  struct {
1929    const base::string16 input;
1930    const std::string json;
1931    const std::string matches[6];
1932  } cases[] = {
1933    // The history results outscore the default verbatim score.  term2 has more
1934    // visits so it outscores term1.  The suggestions are still returned since
1935    // they're server-scored.
1936    { term,
1937      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1938       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1939        "\"google:suggestrelevance\":[1, 2, 3]}]",
1940      { "term2", "term1", "term", "a3", "a2", "a1" } },
1941    // Because we already have three suggestions by the time we see the history
1942    // results, they don't get returned.
1943    { term,
1944      "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
1945       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
1946        "\"google:verbatimrelevance\":1450,"
1947        "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
1948      { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
1949    // If we only have two suggestions, we have room for a history result.
1950    { term,
1951      "[\"term\",[\"a1\", \"a2\"],[],[],"
1952       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
1953        "\"google:verbatimrelevance\":1450,"
1954        "\"google:suggestrelevance\":[1430, 1410]}]",
1955      { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
1956    // If we have more than three suggestions, they should all be returned as
1957    // long as we have enough total space for them.
1958    { term,
1959      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
1960       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
1961        "\"google:verbatimrelevance\":1450,"
1962        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
1963      { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
1964    { term,
1965      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
1966       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
1967                                "\"QUERY\", \"QUERY\"],"
1968        "\"google:verbatimrelevance\":1450,"
1969        "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
1970      { "term", "a1", "a2", "a3", "a4", "a5" } },
1971    { term,
1972      "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
1973       "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
1974        "\"google:verbatimrelevance\":1450,"
1975        "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
1976      { "term", "a1", "a2", "term2", "a3", "a4" } }
1977  };
1978
1979  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1980    QueryForInput(cases[i].input, false, false);
1981    net::TestURLFetcher* fetcher =
1982        test_factory_.GetFetcherByID(
1983            SearchProvider::kDefaultProviderURLFetcherID);
1984    ASSERT_TRUE(fetcher);
1985    fetcher->set_response_code(200);
1986    fetcher->SetResponseString(cases[i].json);
1987    fetcher->delegate()->OnURLFetchComplete(fetcher);
1988    RunTillProviderDone();
1989
1990    const std::string description = "for input with json=" + cases[i].json;
1991    const ACMatches& matches = provider_->matches();
1992
1993    // Ensure no extra matches are present.
1994    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
1995
1996    size_t j = 0;
1997    // Ensure that the returned matches equal the expectations.
1998    for (; j < matches.size(); ++j)
1999      EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
2000                matches[j].contents) << description;
2001    // Ensure that no expected matches are missing.
2002    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2003      EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
2004          "Case # " << i << " " << description;
2005  }
2006}
2007
2008// Verifies suggest relevance behavior for URL input.
2009TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
2010  struct DefaultFetcherUrlInputMatch {
2011    const std::string match_contents;
2012    AutocompleteMatch::Type match_type;
2013    bool allowed_to_be_default_match;
2014  };
2015  const DefaultFetcherUrlInputMatch kEmptyMatch =
2016      { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2017  struct {
2018    const std::string input;
2019    const std::string json;
2020    const DefaultFetcherUrlInputMatch output[4];
2021  } cases[] = {
2022    // Ensure NAVIGATION matches are allowed to be listed first for URL
2023    // input regardless of whether the match is inlineable.  Note that
2024    // non-inlineable matches should not be allowed to be the default match.
2025    { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[],"
2026                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2027                 "\"google:suggestrelevance\":[9999]}]",
2028      { { "b.com",   AutocompleteMatchType::NAVSUGGEST,            false },
2029        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2030        kEmptyMatch, kEmptyMatch } },
2031    { "a.com", "[\"a.com\",[\"https://b.com\"],[],[],"
2032                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2033                 "\"google:suggestrelevance\":[9999]}]",
2034      { { "https://b.com", AutocompleteMatchType::NAVSUGGEST,           false },
2035        { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2036        kEmptyMatch, kEmptyMatch } },
2037    { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2038                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2039                 "\"google:suggestrelevance\":[9999]}]",
2040      { { "a.com/a", AutocompleteMatchType::NAVSUGGEST,            true },
2041        { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2042        kEmptyMatch, kEmptyMatch } },
2043    { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2044                "{\"google:suggesttype\":[\"NAVIGATION\"],"
2045                 "\"google:suggestrelevance\":[9999]}]",
2046      { { "https://a.com", AutocompleteMatchType::NAVSUGGEST,            true },
2047        { "a.com",         AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2048        kEmptyMatch, kEmptyMatch } },
2049
2050    // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL
2051    // input.  SearchProvider disregards search and verbatim suggested
2052    // relevances.
2053    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2054                "{\"google:suggestrelevance\":[9999]}]",
2055      { { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2056        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2057        kEmptyMatch, kEmptyMatch } },
2058    { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2059                "{\"google:suggestrelevance\":[9999]}]",
2060      { { "a.com",   AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,   true },
2061        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2062        kEmptyMatch, kEmptyMatch } },
2063
2064    // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
2065    { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2066                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2067                 "\"google:suggestrelevance\":[9999, 9998]}]",
2068      { { "a.com/b",    AutocompleteMatchType::NAVSUGGEST,            true },
2069        { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2070        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2071        kEmptyMatch } },
2072    { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2073                "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2074                 "\"google:suggestrelevance\":[9998, 9997],"
2075                 "\"google:verbatimrelevance\":9999}]",
2076      { { "a.com/b",    AutocompleteMatchType::NAVSUGGEST,            true },
2077        { "a.com",      AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2078        { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST,        true },
2079        kEmptyMatch } },
2080
2081    // Ensure topmost non-inlineable SUGGEST matches are allowed for URL
2082    // input assuming the top inlineable match is not a query (i.e., is a
2083    // NAVSUGGEST).
2084    { "a.com", "[\"a.com\",[\"info\"],[],[],"
2085                "{\"google:suggestrelevance\":[9999]}]",
2086      { { "info",  AutocompleteMatchType::SEARCH_SUGGEST,        false },
2087        { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2088        kEmptyMatch, kEmptyMatch } },
2089    { "a.com", "[\"a.com\",[\"info\"],[],[],"
2090                "{\"google:suggestrelevance\":[9999]}]",
2091      { { "info",  AutocompleteMatchType::SEARCH_SUGGEST,        false },
2092        { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2093        kEmptyMatch, kEmptyMatch } },
2094  };
2095
2096  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2097    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2098    net::TestURLFetcher* fetcher =
2099        test_factory_.GetFetcherByID(
2100            SearchProvider::kDefaultProviderURLFetcherID);
2101    ASSERT_TRUE(fetcher);
2102    fetcher->set_response_code(200);
2103    fetcher->SetResponseString(cases[i].json);
2104    fetcher->delegate()->OnURLFetchComplete(fetcher);
2105    RunTillProviderDone();
2106
2107    size_t j = 0;
2108    const ACMatches& matches = provider_->matches();
2109    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].output));
2110    // Ensure that the returned matches equal the expectations.
2111    for (; j < matches.size(); ++j) {
2112      EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2113                matches[j].contents);
2114      EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2115      EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2116                matches[j].allowed_to_be_default_match);
2117    }
2118    // Ensure that no expected matches are missing.
2119    for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2120      EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2121      EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2122                cases[i].output[j].match_type);
2123      EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2124    }
2125  }
2126}
2127
2128// A basic test that verifies the field trial triggered parsing logic.
2129TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2130  QueryForInput(ASCIIToUTF16("foo"), false, false);
2131
2132  // Make sure the default providers suggest service was queried.
2133  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2134      SearchProvider::kDefaultProviderURLFetcherID);
2135  ASSERT_TRUE(fetcher);
2136
2137  // Tell the SearchProvider the suggest query is done.
2138  fetcher->set_response_code(200);
2139  fetcher->SetResponseString(
2140      "[\"foo\",[\"foo bar\"],[\"\"],[],"
2141      "{\"google:suggesttype\":[\"QUERY\"],"
2142      "\"google:fieldtrialtriggered\":true}]");
2143  fetcher->delegate()->OnURLFetchComplete(fetcher);
2144  fetcher = NULL;
2145
2146  // Run till the history results complete.
2147  RunTillProviderDone();
2148
2149  {
2150    // Check for the match and field trial triggered bits.
2151    AutocompleteMatch match;
2152    EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
2153    ProvidersInfo providers_info;
2154    provider_->AddProviderInfo(&providers_info);
2155    ASSERT_EQ(1U, providers_info.size());
2156    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2157    EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
2158  }
2159  {
2160    // Reset the session and check that bits are reset.
2161    provider_->ResetSession();
2162    ProvidersInfo providers_info;
2163    provider_->AddProviderInfo(&providers_info);
2164    ASSERT_EQ(1U, providers_info.size());
2165    EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2166    EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
2167  }
2168}
2169
2170// Verifies inline autocompletion of navigational results.
2171TEST_F(SearchProviderTest, NavigationInline) {
2172  struct {
2173    const std::string input;
2174    const std::string url;
2175    // Test the expected fill_into_edit, which may drop "http://".
2176    // Some cases do not trim "http://" to match from the start of the scheme.
2177    const std::string fill_into_edit;
2178    const std::string inline_autocompletion;
2179    const bool allowed_to_be_default_match_in_regular_mode;
2180    const bool allowed_to_be_default_match_in_prevent_inline_mode;
2181  } cases[] = {
2182    // Do not inline matches that do not contain the input; trim http as needed.
2183    { "x",                 "http://www.abc.com",
2184                                  "www.abc.com",  std::string(), false, false },
2185    { "https:",            "http://www.abc.com",
2186                                  "www.abc.com",  std::string(), false, false },
2187    { "http://www.abc.com/a", "http://www.abc.com",
2188                              "http://www.abc.com",  std::string(), false,
2189                                                                    false },
2190    { "http://www.abc.com",   "https://www.abc.com",
2191                              "https://www.abc.com", std::string(), false,
2192                                                                    false },
2193    { "http://abc.com",       "ftp://abc.com",
2194                              "ftp://abc.com",       std::string(), false,
2195                                                                    false },
2196    { "https://www.abc.com",  "http://www.abc.com",
2197                                     "www.abc.com",  std::string(), false,
2198                                                                    false },
2199    { "ftp://abc.com",        "http://abc.com",
2200                                     "abc.com",      std::string(), false,
2201                                                                    false },
2202
2203    // Do not inline matches with invalid input prefixes; trim http as needed.
2204    { "ttp",              "http://www.abc.com",
2205                                 "www.abc.com", std::string(), false, false },
2206    { "://w",             "http://www.abc.com",
2207                                 "www.abc.com", std::string(), false, false },
2208    { "ww.",              "http://www.abc.com",
2209                                 "www.abc.com", std::string(), false, false },
2210    { ".ab",              "http://www.abc.com",
2211                                 "www.abc.com", std::string(), false, false },
2212    { "bc",               "http://www.abc.com",
2213                                 "www.abc.com", std::string(), false, false },
2214    { ".com",             "http://www.abc.com",
2215                                 "www.abc.com", std::string(), false, false },
2216
2217    // Do not inline matches that omit input domain labels; trim http as needed.
2218    { "www.a",            "http://a.com",
2219                                 "a.com",       std::string(), false, false },
2220    { "http://www.a",     "http://a.com",
2221                          "http://a.com",       std::string(), false, false },
2222    { "www.a",            "ftp://a.com",
2223                          "ftp://a.com",        std::string(), false, false },
2224    { "ftp://www.a",      "ftp://a.com",
2225                          "ftp://a.com",        std::string(), false, false },
2226
2227    // Input matching but with nothing to inline will not yield an offset, but
2228    // will be allowed to be default.
2229    { "abc.com",             "http://www.abc.com",
2230                                    "www.abc.com", std::string(), true, true },
2231    { "abc.com/",            "http://www.abc.com",
2232                                    "www.abc.com", std::string(), true, true },
2233    { "http://www.abc.com",  "http://www.abc.com",
2234                             "http://www.abc.com", std::string(), true, true },
2235    { "http://www.abc.com/", "http://www.abc.com",
2236                             "http://www.abc.com", std::string(), true, true },
2237
2238    // Inputs with trailing whitespace should inline when possible.
2239    { "abc.com ",      "http://www.abc.com",
2240                              "www.abc.com",      std::string(), true,  true },
2241    { "abc.com/ ",     "http://www.abc.com",
2242                              "www.abc.com",      std::string(), true,  true },
2243    { "abc.com ",      "http://www.abc.com/bar",
2244                              "www.abc.com/bar",  "/bar",        false, false },
2245
2246    // Inline matches when the input is a leading substring of the scheme.
2247    { "h",             "http://www.abc.com",
2248                       "http://www.abc.com", "ttp://www.abc.com", true, false },
2249    { "http",          "http://www.abc.com",
2250                       "http://www.abc.com", "://www.abc.com",    true, false },
2251
2252    // Inline matches when the input is a leading substring of the full URL.
2253    { "http:",             "http://www.abc.com",
2254                           "http://www.abc.com", "//www.abc.com", true, false },
2255    { "http://w",          "http://www.abc.com",
2256                           "http://www.abc.com", "ww.abc.com",    true, false },
2257    { "http://www.",       "http://www.abc.com",
2258                           "http://www.abc.com", "abc.com",       true, false },
2259    { "http://www.ab",     "http://www.abc.com",
2260                           "http://www.abc.com", "c.com",         true, false },
2261    { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2262                              "http://www.abc.com/path/file.htm?q=x#foo",
2263                                                  "ath/file.htm?q=x#foo",
2264                                                                  true, false },
2265    { "http://abc.com/p",     "http://abc.com/path/file.htm?q=x#foo",
2266                              "http://abc.com/path/file.htm?q=x#foo",
2267                                              "ath/file.htm?q=x#foo",
2268                                                                  true, false},
2269
2270    // Inline matches with valid URLPrefixes; only trim "http://".
2271    { "w",               "http://www.abc.com",
2272                                "www.abc.com", "ww.abc.com", true, false },
2273    { "www.a",           "http://www.abc.com",
2274                                "www.abc.com", "bc.com",     true, false },
2275    { "abc",             "http://www.abc.com",
2276                                "www.abc.com", ".com",       true, false },
2277    { "abc.c",           "http://www.abc.com",
2278                                "www.abc.com", "om",         true, false },
2279    { "abc.com/p",       "http://www.abc.com/path/file.htm?q=x#foo",
2280                                "www.abc.com/path/file.htm?q=x#foo",
2281                                             "ath/file.htm?q=x#foo",
2282                                                             true, false },
2283    { "abc.com/p",       "http://abc.com/path/file.htm?q=x#foo",
2284                                "abc.com/path/file.htm?q=x#foo",
2285                                         "ath/file.htm?q=x#foo",
2286                                                             true, false },
2287
2288    // Inline matches using the maximal URLPrefix components.
2289    { "h",               "http://help.com",
2290                                "help.com", "elp.com",     true, false },
2291    { "http",            "http://http.com",
2292                                "http.com", ".com",        true, false },
2293    { "h",               "http://www.help.com",
2294                                "www.help.com", "elp.com", true, false },
2295    { "http",            "http://www.http.com",
2296                                "www.http.com", ".com",    true, false },
2297    { "w",               "http://www.www.com",
2298                                "www.www.com",  "ww.com",  true, false },
2299
2300    // Test similar behavior for the ftp and https schemes.
2301    { "ftp://www.ab",  "ftp://www.abc.com/path/file.htm?q=x#foo",
2302                       "ftp://www.abc.com/path/file.htm?q=x#foo",
2303                                  "c.com/path/file.htm?q=x#foo",  true, false },
2304    { "www.ab",        "ftp://www.abc.com/path/file.htm?q=x#foo",
2305                       "ftp://www.abc.com/path/file.htm?q=x#foo",
2306                                   "c.com/path/file.htm?q=x#foo", true, false },
2307    { "ab",            "ftp://www.abc.com/path/file.htm?q=x#foo",
2308                       "ftp://www.abc.com/path/file.htm?q=x#foo",
2309                                   "c.com/path/file.htm?q=x#foo", true, false },
2310    { "ab",            "ftp://abc.com/path/file.htm?q=x#foo",
2311                       "ftp://abc.com/path/file.htm?q=x#foo",
2312                               "c.com/path/file.htm?q=x#foo",     true, false },
2313    { "https://www.ab",  "https://www.abc.com/path/file.htm?q=x#foo",
2314                         "https://www.abc.com/path/file.htm?q=x#foo",
2315                                       "c.com/path/file.htm?q=x#foo",
2316                                                                  true, false },
2317    { "www.ab",      "https://www.abc.com/path/file.htm?q=x#foo",
2318                     "https://www.abc.com/path/file.htm?q=x#foo",
2319                                   "c.com/path/file.htm?q=x#foo", true, false },
2320    { "ab",          "https://www.abc.com/path/file.htm?q=x#foo",
2321                     "https://www.abc.com/path/file.htm?q=x#foo",
2322                                   "c.com/path/file.htm?q=x#foo", true, false },
2323    { "ab",          "https://abc.com/path/file.htm?q=x#foo",
2324                     "https://abc.com/path/file.htm?q=x#foo",
2325                               "c.com/path/file.htm?q=x#foo",     true, false },
2326
2327    // Forced query input should inline and retain the "?" prefix.
2328    { "?http://www.ab",  "http://www.abc.com",
2329                        "?http://www.abc.com", "c.com", true, false },
2330    { "?www.ab",         "http://www.abc.com",
2331                               "?www.abc.com", "c.com", true, false },
2332    { "?ab",             "http://www.abc.com",
2333                               "?www.abc.com", "c.com", true, false },
2334    { "?abc.com",        "http://www.abc.com",
2335                               "?www.abc.com", "",      true, true },
2336  };
2337
2338  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2339    // First test regular mode.
2340    QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2341    AutocompleteMatch match(
2342        provider_->NavigationToMatch(SearchProvider::NavigationResult(
2343            *provider_.get(), GURL(cases[i].url),
2344            AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2345            false, 0, false, ASCIIToUTF16(cases[i].input), std::string())));
2346    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2347              match.inline_autocompletion);
2348    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
2349    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode,
2350              match.allowed_to_be_default_match);
2351
2352    // Then test prevent-inline-autocomplete mode.
2353    QueryForInput(ASCIIToUTF16(cases[i].input), true, false);
2354    AutocompleteMatch match_prevent_inline(
2355        provider_->NavigationToMatch(SearchProvider::NavigationResult(
2356            *provider_.get(), GURL(cases[i].url),
2357            AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2358            false, 0, false, ASCIIToUTF16(cases[i].input), std::string())));
2359    EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2360              match_prevent_inline.inline_autocompletion);
2361    EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit),
2362              match_prevent_inline.fill_into_edit);
2363    EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode,
2364              match_prevent_inline.allowed_to_be_default_match);
2365  }
2366}
2367
2368// Verifies that "http://" is not trimmed for input that is a leading substring.
2369TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
2370  const base::string16 input(ASCIIToUTF16("ht"));
2371  const base::string16 url(ASCIIToUTF16("http://a.com"));
2372  const SearchProvider::NavigationResult result(
2373      *provider_.get(), GURL(url), AutocompleteMatchType::NAVSUGGEST,
2374      base::string16(), std::string(), false, 0, false, input, std::string());
2375
2376  // Check the offset and strings when inline autocompletion is allowed.
2377  QueryForInput(input, false, false);
2378  AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
2379  EXPECT_EQ(url, match_inline.fill_into_edit);
2380  EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
2381  EXPECT_TRUE(match_inline.allowed_to_be_default_match);
2382  EXPECT_EQ(url, match_inline.contents);
2383
2384  // Check the same strings when inline autocompletion is prevented.
2385  QueryForInput(input, true, false);
2386  AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
2387  EXPECT_EQ(url, match_prevent.fill_into_edit);
2388  EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
2389  EXPECT_EQ(url, match_prevent.contents);
2390}
2391
2392// Verifies that input "w" marks a more significant domain label than "www.".
2393TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
2394  QueryForInput(ASCIIToUTF16("w"), false, false);
2395  AutocompleteMatch match(
2396      provider_->NavigationToMatch(SearchProvider::NavigationResult(
2397          *provider_.get(), GURL("http://www.wow.com"),
2398          AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2399          false, 0, false, ASCIIToUTF16("w"), std::string())));
2400  EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
2401  EXPECT_TRUE(match.allowed_to_be_default_match);
2402  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
2403  EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
2404
2405  // Ensure that the match for input "w" is marked on "wow" and not "www".
2406  ASSERT_EQ(3U, match.contents_class.size());
2407  EXPECT_EQ(0U, match.contents_class[0].offset);
2408  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2409            match.contents_class[0].style);
2410  EXPECT_EQ(4U, match.contents_class[1].offset);
2411  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
2412            AutocompleteMatch::ACMatchClassification::MATCH,
2413            match.contents_class[1].style);
2414  EXPECT_EQ(5U, match.contents_class[2].offset);
2415  EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2416            match.contents_class[2].style);
2417}
2418
2419#if !defined(OS_WIN)
2420// Verify entity suggestion parsing.
2421TEST_F(SearchProviderTest, ParseEntitySuggestion) {
2422  struct Match {
2423    std::string contents;
2424    std::string description;
2425    std::string query_params;
2426    std::string fill_into_edit;
2427    AutocompleteMatchType::Type type;
2428  };
2429  const Match kEmptyMatch = {
2430    kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
2431    AutocompleteMatchType::NUM_TYPES};
2432
2433  struct {
2434    const std::string input_text;
2435    const std::string response_json;
2436    const Match matches[5];
2437  } cases[] = {
2438    // A query and an entity suggestion with different search terms.
2439    { "x",
2440      "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[],"
2441      " {\"google:suggestdetail\":[{},"
2442      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
2443      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2444      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2445        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
2446        { "xy", "A", "p=v", "yy",
2447          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
2448        kEmptyMatch,
2449        kEmptyMatch
2450      },
2451    },
2452    // A query and an entity suggestion with same search terms.
2453    { "x",
2454      "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
2455      " {\"google:suggestdetail\":[{},"
2456      "   {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
2457      "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2458      { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2459        { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
2460        { "xy", "A", "p=v", "xy",
2461          AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
2462        kEmptyMatch,
2463        kEmptyMatch
2464      },
2465    },
2466  };
2467  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2468    QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
2469
2470    // Set up a default fetcher with provided results.
2471    net::TestURLFetcher* fetcher =
2472        test_factory_.GetFetcherByID(
2473            SearchProvider::kDefaultProviderURLFetcherID);
2474    ASSERT_TRUE(fetcher);
2475    fetcher->set_response_code(200);
2476    fetcher->SetResponseString(cases[i].response_json);
2477    fetcher->delegate()->OnURLFetchComplete(fetcher);
2478
2479    RunTillProviderDone();
2480
2481    const ACMatches& matches = provider_->matches();
2482    ASSERT_FALSE(matches.empty());
2483
2484    SCOPED_TRACE("for input with json = " + cases[i].response_json);
2485
2486    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2487    size_t j = 0;
2488    // Ensure that the returned matches equal the expectations.
2489    for (; j < matches.size(); ++j) {
2490      const Match& match = cases[i].matches[j];
2491      SCOPED_TRACE(" and match index: " + base::IntToString(j));
2492      EXPECT_EQ(match.contents,
2493                base::UTF16ToUTF8(matches[j].contents));
2494      EXPECT_EQ(match.description,
2495                base::UTF16ToUTF8(matches[j].description));
2496      EXPECT_EQ(match.query_params,
2497                matches[j].search_terms_args->suggest_query_params);
2498      EXPECT_EQ(match.fill_into_edit,
2499                base::UTF16ToUTF8(matches[j].fill_into_edit));
2500      EXPECT_EQ(match.type, matches[j].type);
2501    }
2502    // Ensure that no expected matches are missing.
2503    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
2504      SCOPED_TRACE(" and match index: " + base::IntToString(j));
2505      EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
2506      EXPECT_EQ(cases[i].matches[j].description, kNotApplicable);
2507      EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
2508      EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
2509      EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
2510    }
2511  }
2512}
2513#endif  // !defined(OS_WIN)
2514
2515
2516// A basic test that verifies the prefetch metadata parsing logic.
2517TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
2518  struct Match {
2519    std::string contents;
2520    bool allowed_to_be_prefetched;
2521    AutocompleteMatchType::Type type;
2522    bool from_keyword;
2523  };
2524  const Match kEmptyMatch = { kNotApplicable,
2525                              false,
2526                              AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2527                              false };
2528
2529  struct {
2530    const std::string input_text;
2531    bool prefer_keyword_provider_results;
2532    const std::string default_provider_response_json;
2533    const std::string keyword_provider_response_json;
2534    const Match matches[5];
2535  } cases[] = {
2536    // Default provider response does not have prefetch details. Ensure that the
2537    // suggestions are not marked as prefetch query.
2538    { "a",
2539      false,
2540      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2541      std::string(),
2542      { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2543        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2544        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2545        kEmptyMatch,
2546        kEmptyMatch
2547      },
2548    },
2549    // Ensure that default provider suggest response prefetch details are
2550    // parsed and recorded in AutocompleteMatch.
2551    { "ab",
2552      false,
2553      "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
2554          "{\"google:clientdata\":{\"phi\": 0},"
2555          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
2556          "\"google:suggestrelevance\":[999, 12, 1]}]",
2557      std::string(),
2558      { { "ab",    false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2559        { "abc",   true,  AutocompleteMatchType::SEARCH_SUGGEST, false },
2560        { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2561        { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2562        kEmptyMatch
2563      },
2564    },
2565    // Default provider suggest response has prefetch details.
2566    // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
2567    // the same query string. Ensure that the prefetch details from
2568    // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
2569    { "ab",
2570      false,
2571      "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
2572          "{\"google:clientdata\":{\"phi\": 0},"
2573          "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2574          "\"google:suggestrelevance\":[99, 98]}]",
2575      std::string(),
2576      { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2577        {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2578        kEmptyMatch,
2579        kEmptyMatch,
2580        kEmptyMatch
2581      },
2582    },
2583    // Default provider response has prefetch details. We prefer keyword
2584    // provider results. Ensure that prefetch bit for a suggestion from the
2585    // default search provider does not get copied onto a higher-scoring match
2586    // for the same query string from the keyword provider.
2587    { "k a",
2588      true,
2589      "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
2590          "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2591          "\"google:suggestrelevance\":[9, 12]}]",
2592      "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2593      { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
2594        { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2595        { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2596        { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
2597        { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
2598      },
2599    }
2600  };
2601
2602  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2603    QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
2604                  cases[i].prefer_keyword_provider_results);
2605
2606    // Set up a default fetcher with provided results.
2607    net::TestURLFetcher* fetcher =
2608        test_factory_.GetFetcherByID(
2609            SearchProvider::kDefaultProviderURLFetcherID);
2610    ASSERT_TRUE(fetcher);
2611    fetcher->set_response_code(200);
2612    fetcher->SetResponseString(cases[i].default_provider_response_json);
2613    fetcher->delegate()->OnURLFetchComplete(fetcher);
2614
2615    if (cases[i].prefer_keyword_provider_results) {
2616      // Set up a keyword fetcher with provided results.
2617      net::TestURLFetcher* keyword_fetcher =
2618          test_factory_.GetFetcherByID(
2619              SearchProvider::kKeywordProviderURLFetcherID);
2620      ASSERT_TRUE(keyword_fetcher);
2621      keyword_fetcher->set_response_code(200);
2622      keyword_fetcher->SetResponseString(
2623          cases[i].keyword_provider_response_json);
2624      keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2625      keyword_fetcher = NULL;
2626    }
2627
2628    RunTillProviderDone();
2629
2630    const std::string description =
2631        "for input with json =" + cases[i].default_provider_response_json;
2632    const ACMatches& matches = provider_->matches();
2633    // The top match must inline and score as highly as calculated verbatim.
2634    ASSERT_FALSE(matches.empty());
2635    EXPECT_GE(matches[0].relevance, 1300);
2636
2637    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2638    // Ensure that the returned matches equal the expectations.
2639    for (size_t j = 0; j < matches.size(); ++j) {
2640      SCOPED_TRACE(description);
2641      EXPECT_EQ(cases[i].matches[j].contents,
2642                base::UTF16ToUTF8(matches[j].contents));
2643      EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
2644                SearchProvider::ShouldPrefetch(matches[j]));
2645      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2646      EXPECT_EQ(cases[i].matches[j].from_keyword,
2647                matches[j].keyword == ASCIIToUTF16("k"));
2648    }
2649  }
2650}
2651
2652TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) {
2653  ClearAllResults();
2654
2655  std::string input_str("abc");
2656  QueryForInput(ASCIIToUTF16(input_str), false, false);
2657
2658  // Set up a default fetcher with provided results.
2659  net::TestURLFetcher* fetcher =
2660      test_factory_.GetFetcherByID(
2661          SearchProvider::kDefaultProviderURLFetcherID);
2662  ASSERT_TRUE(fetcher);
2663  fetcher->set_response_code(200);
2664  fetcher->SetResponseString("this is a bad non-json response");
2665  fetcher->delegate()->OnURLFetchComplete(fetcher);
2666
2667  RunTillProviderDone();
2668
2669  const ACMatches& matches = provider_->matches();
2670
2671  // Should have exactly one "search what you typed" match
2672  ASSERT_TRUE(matches.size() == 1);
2673  EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents));
2674  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2675            matches[0].type);
2676}
2677
2678// A basic test that verifies that the XSSI guarded JSON response is parsed
2679// correctly.
2680TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) {
2681  struct Match {
2682    std::string contents;
2683    AutocompleteMatchType::Type type;
2684  };
2685  const Match kEmptyMatch = {
2686      kNotApplicable, AutocompleteMatchType::NUM_TYPES
2687  };
2688
2689  struct {
2690    const std::string input_text;
2691    const std::string default_provider_response_json;
2692    const Match matches[4];
2693  } cases[] = {
2694    // No XSSI guard.
2695    { "a",
2696      "[\"a\",[\"b\", \"c\"],[],[],"
2697      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2698      "\"google:suggestrelevance\":[1, 2]}]",
2699      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2700        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
2701        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
2702        kEmptyMatch,
2703      },
2704    },
2705    // Standard XSSI guard - )]}'\n.
2706    { "a",
2707      ")]}'\n[\"a\",[\"b\", \"c\"],[],[],"
2708      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2709      "\"google:suggestrelevance\":[1, 2]}]",
2710      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2711        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
2712        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
2713        kEmptyMatch,
2714      },
2715    },
2716    // Modified XSSI guard - contains "[".
2717    { "a",
2718      ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[],"
2719      "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
2720      "\"google:suggestrelevance\":[1, 2]}]",
2721      { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2722        { "c", AutocompleteMatchType::SEARCH_SUGGEST },
2723        { "b", AutocompleteMatchType::SEARCH_SUGGEST },
2724        kEmptyMatch,
2725      },
2726    },
2727  };
2728
2729  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2730    ClearAllResults();
2731    QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
2732
2733    // Set up a default fetcher with provided results.
2734    net::TestURLFetcher* fetcher =
2735        test_factory_.GetFetcherByID(
2736            SearchProvider::kDefaultProviderURLFetcherID);
2737    ASSERT_TRUE(fetcher);
2738    fetcher->set_response_code(200);
2739    fetcher->SetResponseString(cases[i].default_provider_response_json);
2740    fetcher->delegate()->OnURLFetchComplete(fetcher);
2741
2742    RunTillProviderDone();
2743
2744    const ACMatches& matches = provider_->matches();
2745    // The top match must inline and score as highly as calculated verbatim.
2746    ASSERT_FALSE(matches.empty());
2747    EXPECT_GE(matches[0].relevance, 1300);
2748
2749    SCOPED_TRACE("for case: " + base::IntToString(i));
2750    ASSERT_LE(matches.size(), ARRAYSIZE_UNSAFE(cases[i].matches));
2751    size_t j = 0;
2752    // Ensure that the returned matches equal the expectations.
2753    for (; j < matches.size(); ++j) {
2754      SCOPED_TRACE("and match: " + base::IntToString(j));
2755      EXPECT_EQ(cases[i].matches[j].contents,
2756                base::UTF16ToUTF8(matches[j].contents));
2757      EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2758    }
2759    for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
2760      SCOPED_TRACE("and match: " + base::IntToString(j));
2761      EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
2762      EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
2763    }
2764  }
2765}
2766
2767// Test that deletion url gets set on an AutocompleteMatch when available for a
2768// personalized query or a personalized URL.
2769TEST_F(SearchProviderTest, ParseDeletionUrl) {
2770   struct Match {
2771     std::string contents;
2772     std::string deletion_url;
2773     AutocompleteMatchType::Type type;
2774   };
2775
2776   const Match kEmptyMatch = {
2777       kNotApplicable, "", AutocompleteMatchType::NUM_TYPES
2778   };
2779
2780   const char* url[] = {
2781    "http://defaultturl/complete/deleteitems"
2782       "?delq=ab&client=chrome&deltok=xsrf124",
2783    "http://defaultturl/complete/deleteitems"
2784       "?delq=www.amazon.com&client=chrome&deltok=xsrf123",
2785     };
2786
2787   struct {
2788       const std::string input_text;
2789       const std::string response_json;
2790       const Match matches[5];
2791     } cases[] = {
2792       // A deletion URL on a personalized query should be reflected in the
2793       // resulting AutocompleteMatch.
2794       { "a",
2795         "[\"a\",[\"ab\", \"ac\",\"www.amazon.com\"],[],[],"
2796         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
2797          "\"PERSONALIZED_NAVIGATION\"],"
2798         "\"google:suggestrelevance\":[3, 2, 1],"
2799         "\"google:suggestdetail\":[{\"du\":"
2800         "\"/complete/deleteitems?delq=ab&client=chrome"
2801          "&deltok=xsrf124\"}, {}, {\"du\":"
2802         "\"/complete/deleteitems?delq=www.amazon.com&"
2803         "client=chrome&deltok=xsrf123\"}]}]",
2804         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2805           { "ab", url[0], AutocompleteMatchType::SEARCH_SUGGEST },
2806           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
2807           { "www.amazon.com", url[1],
2808              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
2809           kEmptyMatch,
2810         },
2811       },
2812       // Personalized queries or a personalized URL without deletion URLs
2813       // shouldn't cause errors.
2814       { "a",
2815         "[\"a\",[\"ab\", \"ac\"],[],[],"
2816         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
2817         "\"PERSONALIZED_NAVIGATION\"],"
2818         "\"google:suggestrelevance\":[1, 2],"
2819         "\"google:suggestdetail\":[{}, {}]}]",
2820         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2821           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
2822           { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
2823           { "www.amazon.com", "",
2824              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
2825           kEmptyMatch,
2826         },
2827       },
2828       // Personalized queries or a personalized URL without
2829       // google:suggestdetail shouldn't cause errors.
2830       { "a",
2831         "[\"a\",[\"ab\", \"ac\"],[],[],"
2832         "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
2833         "\"PERSONALIZED_NAVIGATION\"],"
2834         "\"google:suggestrelevance\":[1, 2]}]",
2835         { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2836           { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
2837           { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
2838           { "www.amazon.com", "",
2839              AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
2840           kEmptyMatch,
2841         },
2842       },
2843     };
2844
2845     for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2846       QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
2847
2848       net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2849           SearchProvider::kDefaultProviderURLFetcherID);
2850       ASSERT_TRUE(fetcher);
2851       fetcher->set_response_code(200);
2852       fetcher->SetResponseString(cases[i].response_json);
2853       fetcher->delegate()->OnURLFetchComplete(fetcher);
2854
2855       RunTillProviderDone();
2856
2857       const ACMatches& matches = provider_->matches();
2858       ASSERT_FALSE(matches.empty());
2859
2860       SCOPED_TRACE("for input with json = " + cases[i].response_json);
2861
2862       for (size_t j = 0; j < matches.size(); ++j) {
2863         const Match& match = cases[i].matches[j];
2864         SCOPED_TRACE(" and match index: " + base::IntToString(j));
2865         EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents));
2866         EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo(
2867             "deletion_url"));
2868       }
2869     }
2870}
2871
2872TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
2873  profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false);
2874  base::string16 term = term1_.substr(0, term1_.length() - 1);
2875  QueryForInput(term, true, false);
2876  ASSERT_FALSE(provider_->matches().empty());
2877  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2878            provider_->matches()[0].type);
2879  ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
2880  EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
2881
2882  profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true);
2883  term = term1_.substr(0, term1_.length() - 1);
2884  QueryForInput(term, true, false);
2885  ASSERT_FALSE(provider_->matches().empty());
2886  EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2887            provider_->matches()[0].type);
2888  ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
2889  EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
2890}
2891
2892TEST_F(SearchProviderTest, CanSendURL) {
2893  TemplateURLData template_url_data;
2894  template_url_data.short_name = ASCIIToUTF16("t");
2895  template_url_data.SetURL("http://www.google.com/{searchTerms}");
2896  template_url_data.suggestions_url = "http://www.google.com/{searchTerms}";
2897  template_url_data.instant_url = "http://does/not/exist?strk=1";
2898  template_url_data.search_terms_replacement_key = "strk";
2899  template_url_data.id = SEARCH_ENGINE_GOOGLE;
2900  TemplateURL google_template_url(template_url_data);
2901
2902  // Create field trial.
2903  base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial(
2904      "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
2905  field_trial->group();
2906
2907  // Not signed in.
2908  EXPECT_FALSE(SearchProvider::CanSendURL(
2909      GURL("http://www.google.com/search"),
2910      GURL("https://www.google.com/complete/search"), &google_template_url,
2911      metrics::OmniboxEventProto::OTHER, &profile_));
2912  SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_);
2913  signin->SetAuthenticatedUsername("test");
2914
2915  // All conditions should be met.
2916  EXPECT_TRUE(SearchProvider::CanSendURL(
2917      GURL("http://www.google.com/search"),
2918      GURL("https://www.google.com/complete/search"), &google_template_url,
2919      metrics::OmniboxEventProto::OTHER, &profile_));
2920
2921  // Not in field trial.
2922  ResetFieldTrialList();
2923  EXPECT_FALSE(SearchProvider::CanSendURL(
2924      GURL("http://www.google.com/search"),
2925      GURL("https://www.google.com/complete/search"), &google_template_url,
2926      metrics::OmniboxEventProto::OTHER, &profile_));
2927  field_trial = base::FieldTrialList::CreateFieldTrial(
2928      "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
2929  field_trial->group();
2930
2931  // Invalid page URL.
2932  EXPECT_FALSE(SearchProvider::CanSendURL(
2933      GURL("badpageurl"),
2934      GURL("https://www.google.com/complete/search"), &google_template_url,
2935      metrics::OmniboxEventProto::OTHER, &profile_));
2936
2937  // Invalid page classification.
2938  EXPECT_FALSE(SearchProvider::CanSendURL(
2939      GURL("http://www.google.com/search"),
2940      GURL("https://www.google.com/complete/search"), &google_template_url,
2941      metrics::OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
2942      &profile_));
2943
2944  // Invalid page classification.
2945  EXPECT_FALSE(SearchProvider::CanSendURL(
2946      GURL("http://www.google.com/search"),
2947      GURL("https://www.google.com/complete/search"), &google_template_url,
2948      metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
2949      &profile_));
2950
2951  // HTTPS page URL on same domain as provider.
2952  EXPECT_TRUE(SearchProvider::CanSendURL(
2953      GURL("https://www.google.com/search"),
2954      GURL("https://www.google.com/complete/search"),
2955      &google_template_url, metrics::OmniboxEventProto::OTHER, &profile_));
2956
2957  // Non-HTTP[S] page URL on same domain as provider.
2958  EXPECT_FALSE(SearchProvider::CanSendURL(
2959      GURL("ftp://www.google.com/search"),
2960      GURL("https://www.google.com/complete/search"), &google_template_url,
2961      metrics::OmniboxEventProto::OTHER, &profile_));
2962
2963  // Non-HTTP page URL on different domain.
2964  EXPECT_FALSE(SearchProvider::CanSendURL(
2965      GURL("https://www.notgoogle.com/search"),
2966      GURL("https://www.google.com/complete/search"), &google_template_url,
2967      metrics::OmniboxEventProto::OTHER, &profile_));
2968
2969  // Non-HTTPS provider.
2970  EXPECT_FALSE(SearchProvider::CanSendURL(
2971      GURL("http://www.google.com/search"),
2972      GURL("http://www.google.com/complete/search"), &google_template_url,
2973      metrics::OmniboxEventProto::OTHER, &profile_));
2974
2975  // Suggest disabled.
2976  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false);
2977  EXPECT_FALSE(SearchProvider::CanSendURL(
2978      GURL("http://www.google.com/search"),
2979      GURL("https://www.google.com/complete/search"), &google_template_url,
2980      metrics::OmniboxEventProto::OTHER, &profile_));
2981  profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
2982
2983  // Incognito.
2984  EXPECT_FALSE(SearchProvider::CanSendURL(
2985      GURL("http://www.google.com/search"),
2986      GURL("https://www.google.com/complete/search"), &google_template_url,
2987      metrics::OmniboxEventProto::OTHER, profile_.GetOffTheRecordProfile()));
2988
2989  // Tab sync not enabled.
2990  profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncKeepEverythingSynced,
2991                                  false);
2992  profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, false);
2993  EXPECT_FALSE(SearchProvider::CanSendURL(
2994      GURL("http://www.google.com/search"),
2995      GURL("https://www.google.com/complete/search"), &google_template_url,
2996      metrics::OmniboxEventProto::OTHER, &profile_));
2997  profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, true);
2998
2999  // Tab sync is encrypted.
3000  ProfileSyncService* service =
3001      ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_);
3002  syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes();
3003  encrypted_types.Put(syncer::SESSIONS);
3004  service->OnEncryptedTypesChanged(encrypted_types, false);
3005  EXPECT_FALSE(SearchProvider::CanSendURL(
3006      GURL("http://www.google.com/search"),
3007      GURL("https://www.google.com/complete/search"), &google_template_url,
3008      metrics::OmniboxEventProto::OTHER, &profile_));
3009  encrypted_types.Remove(syncer::SESSIONS);
3010  service->OnEncryptedTypesChanged(encrypted_types, false);
3011
3012  // Check that there were no side effects from previous tests.
3013  EXPECT_TRUE(SearchProvider::CanSendURL(
3014      GURL("http://www.google.com/search"),
3015      GURL("https://www.google.com/complete/search"), &google_template_url,
3016      metrics::OmniboxEventProto::OTHER, &profile_));
3017}
3018
3019TEST_F(SearchProviderTest, TestDeleteMatch) {
3020  AutocompleteMatch match(provider_, 0, true,
3021                          AutocompleteMatchType::SEARCH_SUGGEST);
3022  match.RecordAdditionalInfo(
3023      SearchProvider::kDeletionUrlKey,
3024      "https://www.google.com/complete/deleteitem?q=foo");
3025
3026  // Test a successful deletion request.
3027  provider_->matches_.push_back(match);
3028  provider_->DeleteMatch(match);
3029  EXPECT_FALSE(provider_->deletion_handlers_.empty());
3030  EXPECT_TRUE(provider_->matches_.empty());
3031  // Set up a default fetcher with provided results.
3032  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3033      SearchProvider::kDeletionURLFetcherID);
3034  ASSERT_TRUE(fetcher);
3035  fetcher->set_response_code(200);
3036  fetcher->delegate()->OnURLFetchComplete(fetcher);
3037  EXPECT_TRUE(provider_->deletion_handlers_.empty());
3038  EXPECT_TRUE(provider_->is_success());
3039
3040  // Test a failing deletion request.
3041  provider_->matches_.push_back(match);
3042  provider_->DeleteMatch(match);
3043  EXPECT_FALSE(provider_->deletion_handlers_.empty());
3044  // Set up a default fetcher with provided results.
3045  fetcher = test_factory_.GetFetcherByID(
3046      SearchProvider::kDeletionURLFetcherID);
3047  ASSERT_TRUE(fetcher);
3048  fetcher->set_response_code(500);
3049  fetcher->delegate()->OnURLFetchComplete(fetcher);
3050  EXPECT_TRUE(provider_->deletion_handlers_.empty());
3051  EXPECT_FALSE(provider_->is_success());
3052}
3053
3054TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) {
3055  GURL term_url(
3056      AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1));
3057  profile_.BlockUntilHistoryProcessesPendingRequests();
3058
3059  AutocompleteMatch games;
3060  QueryForInput(ASCIIToUTF16("fla"), false, false);
3061  profile_.BlockUntilHistoryProcessesPendingRequests();
3062  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
3063  ASSERT_TRUE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
3064
3065  size_t matches_before = provider_->matches().size();
3066  provider_->DeleteMatch(games);
3067  EXPECT_EQ(matches_before - 1, provider_->matches().size());
3068
3069  // Process history deletions.
3070  profile_.BlockUntilHistoryProcessesPendingRequests();
3071
3072  // Check that the match is gone.
3073  QueryForInput(ASCIIToUTF16("fla"), false, false);
3074  profile_.BlockUntilHistoryProcessesPendingRequests();
3075  ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
3076  EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
3077}
3078
3079// Verifies that duplicates are preserved in AddMatchToMap().
3080TEST_F(SearchProviderTest, CheckDuplicateMatchesSaved) {
3081  AddSearchToHistory(default_t_url_, ASCIIToUTF16("a"), 1);
3082  AddSearchToHistory(default_t_url_, ASCIIToUTF16("alpha"), 1);
3083  AddSearchToHistory(default_t_url_, ASCIIToUTF16("avid"), 1);
3084
3085  profile_.BlockUntilHistoryProcessesPendingRequests();
3086  QueryForInput(ASCIIToUTF16("a"), false, false);
3087
3088  // Make sure the default provider's suggest service was queried.
3089  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3090      SearchProvider::kDefaultProviderURLFetcherID);
3091  ASSERT_TRUE(fetcher);
3092
3093  // Tell the SearchProvider the suggest query is done.
3094  fetcher->set_response_code(200);
3095  fetcher->SetResponseString(
3096      "[\"a\",[\"a\", \"alpha\", \"avid\", \"apricot\"],[],[],"
3097      "{\"google:suggestrelevance\":[1450, 1200, 1150, 1100],"
3098      "\"google:verbatimrelevance\":1350}]");
3099  fetcher->delegate()->OnURLFetchComplete(fetcher);
3100  fetcher = NULL;
3101
3102  // Run till the history results complete.
3103  RunTillProviderDone();
3104
3105  AutocompleteMatch verbatim, match_alpha, match_apricot, match_avid;
3106  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
3107  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("alpha"), &match_alpha));
3108  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("apricot"), &match_apricot));
3109  EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("avid"), &match_avid));
3110
3111  // Verbatim match duplicates are added such that each one has a higher
3112  // relevance than the previous one.
3113  EXPECT_EQ(2U, verbatim.duplicate_matches.size());
3114
3115  // Other match duplicates are added in descending relevance order.
3116  EXPECT_EQ(1U, match_alpha.duplicate_matches.size());
3117  EXPECT_EQ(1U, match_avid.duplicate_matches.size());
3118
3119  EXPECT_EQ(0U, match_apricot.duplicate_matches.size());
3120}
3121
3122TEST_F(SearchProviderTest, SuggestQueryUsesToken) {
3123  CommandLine::ForCurrentProcess()->AppendSwitch(
3124      switches::kEnableAnswersInSuggest);
3125
3126  TemplateURLService* turl_model =
3127      TemplateURLServiceFactory::GetForProfile(&profile_);
3128
3129  TemplateURLData data;
3130  data.short_name = ASCIIToUTF16("default");
3131  data.SetKeyword(data.short_name);
3132  data.SetURL("http://example/{searchTerms}{google:sessionToken}");
3133  data.suggestions_url =
3134      "http://suggest/?q={searchTerms}&{google:sessionToken}";
3135  default_t_url_ = new TemplateURL(data);
3136  turl_model->Add(default_t_url_);
3137  turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
3138
3139  base::string16 term = term1_.substr(0, term1_.length() - 1);
3140  QueryForInput(term, false, false);
3141
3142  // Make sure the default provider's suggest service was queried.
3143  net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3144      SearchProvider::kDefaultProviderURLFetcherID);
3145  ASSERT_TRUE(fetcher);
3146
3147  // And the URL matches what we expected.
3148  TemplateURLRef::SearchTermsArgs search_terms_args(term);
3149  search_terms_args.session_token = provider_->current_token_;
3150  GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
3151      search_terms_args, turl_model->search_terms_data()));
3152  EXPECT_EQ(fetcher->GetOriginalURL().spec(), expected_url.spec());
3153
3154  // Complete running the fetcher to clean up.
3155  fetcher->set_response_code(200);
3156  fetcher->delegate()->OnURLFetchComplete(fetcher);
3157  RunTillProviderDone();
3158}
3159
3160TEST_F(SearchProviderTest, SessionToken) {
3161  // Subsequent calls always get the same token.
3162  std::string token = provider_->GetSessionToken();
3163  std::string token2 = provider_->GetSessionToken();
3164  EXPECT_EQ(token, token2);
3165  EXPECT_FALSE(token.empty());
3166
3167  // Calls do not regenerate a token.
3168  provider_->current_token_ = "PRE-EXISTING TOKEN";
3169  token = provider_->GetSessionToken();
3170  EXPECT_EQ(token, "PRE-EXISTING TOKEN");
3171
3172  // ... unless the token has expired.
3173  provider_->current_token_.clear();
3174  const base::TimeDelta kSmallDelta = base::TimeDelta::FromMilliseconds(1);
3175  provider_->token_expiration_time_ = base::TimeTicks::Now() - kSmallDelta;
3176  token = provider_->GetSessionToken();
3177  EXPECT_FALSE(token.empty());
3178  EXPECT_EQ(token, provider_->current_token_);
3179
3180  // The expiration time is always updated.
3181  provider_->GetSessionToken();
3182  base::TimeTicks expiration_time_1 = provider_->token_expiration_time_;
3183  base::PlatformThread::Sleep(kSmallDelta);
3184  provider_->GetSessionToken();
3185  base::TimeTicks expiration_time_2 = provider_->token_expiration_time_;
3186  EXPECT_GT(expiration_time_2, expiration_time_1);
3187  EXPECT_GE(expiration_time_2, expiration_time_1 + kSmallDelta);
3188}
3189