Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / autocomplete / search_provider_unittest.cc
blob22dd22e9437a5cc35bfb21ee8fe1ba7702b635a7
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.
5 #include "components/omnibox/browser/search_provider.h"
7 #include <string>
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/chrome_autocomplete_provider_client.h"
21 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
22 #include "chrome/browser/history/history_service_factory.h"
23 #include "chrome/browser/search_engines/template_url_service_factory.h"
24 #include "chrome/browser/signin/account_tracker_service_factory.h"
25 #include "chrome/browser/signin/signin_manager_factory.h"
26 #include "chrome/browser/sync/profile_sync_service.h"
27 #include "chrome/browser/sync/profile_sync_service_factory.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/common/pref_names.h"
30 #include "chrome/test/base/testing_browser_process.h"
31 #include "chrome/test/base/testing_profile.h"
32 #include "components/google/core/browser/google_switches.h"
33 #include "components/history/core/browser/history_service.h"
34 #include "components/metrics/proto/omnibox_event.pb.h"
35 #include "components/omnibox/browser/autocomplete_controller.h"
36 #include "components/omnibox/browser/autocomplete_input.h"
37 #include "components/omnibox/browser/autocomplete_match.h"
38 #include "components/omnibox/browser/autocomplete_provider.h"
39 #include "components/omnibox/browser/autocomplete_provider_listener.h"
40 #include "components/omnibox/browser/history_url_provider.h"
41 #include "components/omnibox/browser/omnibox_field_trial.h"
42 #include "components/omnibox/browser/omnibox_switches.h"
43 #include "components/omnibox/browser/suggestion_answer.h"
44 #include "components/search_engines/search_engine_type.h"
45 #include "components/search_engines/search_engines_switches.h"
46 #include "components/search_engines/search_terms_data.h"
47 #include "components/search_engines/template_url.h"
48 #include "components/search_engines/template_url_service.h"
49 #include "components/signin/core/browser/account_tracker_service.h"
50 #include "components/signin/core/browser/signin_manager.h"
51 #include "components/sync_driver/pref_names.h"
52 #include "components/variations/entropy_provider.h"
53 #include "components/variations/variations_associated_data.h"
54 #include "content/public/test/test_browser_thread_bundle.h"
55 #include "net/url_request/test_url_fetcher_factory.h"
56 #include "net/url_request/url_request_status.h"
57 #include "testing/gtest/include/gtest/gtest.h"
59 using base::ASCIIToUTF16;
61 namespace {
63 // Returns the first match in |matches| with |allowed_to_be_default_match|
64 // set to true.
65 ACMatches::const_iterator FindDefaultMatch(const ACMatches& matches) {
66 ACMatches::const_iterator it = matches.begin();
67 while ((it != matches.end()) && !it->allowed_to_be_default_match)
68 ++it;
69 return it;
72 class SuggestionDeletionHandler;
73 class SearchProviderForTest : public SearchProvider {
74 public:
75 SearchProviderForTest(ChromeAutocompleteProviderClient* client,
76 AutocompleteProviderListener* listener,
77 Profile* profile);
78 bool is_success() { return is_success_; }
80 protected:
81 ~SearchProviderForTest() override;
83 private:
84 void RecordDeletionResult(bool success) override;
85 bool is_success_;
86 DISALLOW_COPY_AND_ASSIGN(SearchProviderForTest);
89 SearchProviderForTest::SearchProviderForTest(
90 ChromeAutocompleteProviderClient* client,
91 AutocompleteProviderListener* listener,
92 Profile* profile)
93 : SearchProvider(client, listener), is_success_(false) {
96 SearchProviderForTest::~SearchProviderForTest() {
99 void SearchProviderForTest::RecordDeletionResult(bool success) {
100 is_success_ = success;
103 } // namespace
105 // SearchProviderTest ---------------------------------------------------------
107 // The following environment is configured for these tests:
108 // . The TemplateURL default_t_url_ is set as the default provider.
109 // . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This
110 // TemplateURL has a valid suggest and search URL.
111 // . The URL created by using the search term term1_ with default_t_url_ is
112 // added to history.
113 // . The URL created by using the search term keyword_term_ with keyword_t_url_
114 // is added to history.
115 // . test_factory_ is set as the URLFetcherFactory.
116 class SearchProviderTest : public testing::Test,
117 public AutocompleteProviderListener {
118 public:
119 struct ResultInfo {
120 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES),
121 allowed_to_be_default_match(false) {
123 ResultInfo(GURL gurl,
124 AutocompleteMatch::Type result_type,
125 bool allowed_to_be_default_match,
126 base::string16 fill_into_edit)
127 : gurl(gurl),
128 result_type(result_type),
129 allowed_to_be_default_match(allowed_to_be_default_match),
130 fill_into_edit(fill_into_edit) {
133 const GURL gurl;
134 const AutocompleteMatch::Type result_type;
135 const bool allowed_to_be_default_match;
136 const base::string16 fill_into_edit;
139 struct TestData {
140 const base::string16 input;
141 const size_t num_results;
142 const ResultInfo output[3];
145 struct ExpectedMatch {
146 std::string contents;
147 bool allowed_to_be_default_match;
150 SearchProviderTest()
151 : default_t_url_(NULL),
152 term1_(ASCIIToUTF16("term1")),
153 keyword_t_url_(NULL),
154 keyword_term_(ASCIIToUTF16("keyword")),
155 run_loop_(NULL) {
156 ResetFieldTrialList();
159 // See description above class for what this registers.
160 void SetUp() override;
161 void TearDown() override;
163 void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
165 protected:
166 // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
167 scoped_ptr<base::FieldTrialList> field_trial_list_;
169 // Default values used for testing.
170 static const std::string kNotApplicable;
171 static const ExpectedMatch kEmptyExpectedMatch;
173 // Adds a search for |term|, using the engine |t_url| to the history, and
174 // returns the URL for that search.
175 GURL AddSearchToHistory(TemplateURL* t_url, base::string16 term, int visit_count);
177 // Looks for a match in |provider_| with |contents| equal to |contents|.
178 // Sets |match| to it if found. Returns whether |match| was set.
179 bool FindMatchWithContents(const base::string16& contents,
180 AutocompleteMatch* match);
182 // Looks for a match in |provider_| with destination |url|. Sets |match| to
183 // it if found. Returns whether |match| was set.
184 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match);
186 // AutocompleteProviderListener:
187 // If we're waiting for the provider to finish, this exits the message loop.
188 void OnProviderUpdate(bool updated_matches) override;
190 // Runs a nested message loop until provider_ is done. The message loop is
191 // exited by way of OnProviderUpdate.
192 void RunTillProviderDone();
194 // Invokes Start on provider_, then runs all pending tasks.
195 void QueryForInput(const base::string16& text,
196 bool prevent_inline_autocomplete,
197 bool prefer_keyword);
199 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
200 // non-NULL, sets it to the "what you typed" entry for |text|.
201 void QueryForInputAndSetWYTMatch(const base::string16& text,
202 AutocompleteMatch* wyt_match);
204 // Calls QueryForInput(), sets the JSON responses for the default and keyword
205 // fetchers, and waits until the responses have been returned and the matches
206 // returned. Use empty responses for each fetcher that shouldn't be set up /
207 // configured.
208 void QueryForInputAndWaitForFetcherResponses(
209 const base::string16& text,
210 const bool prefer_keyword,
211 const std::string& default_fetcher_response,
212 const std::string& keyword_fetcher_response);
214 // Notifies the URLFetcher for the suggest query corresponding to the default
215 // search provider that it's done.
216 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
217 void FinishDefaultSuggestQuery();
219 // Runs SearchProvider on |input|, for which the suggest server replies
220 // with |json|, and expects that the resulting matches' contents equals
221 // that in |matches|. An empty entry in |matches| means no match should
222 // be returned in that position. Reports any errors with a message that
223 // includes |error_description|.
224 void ForcedQueryTestHelper(const std::string& input,
225 const std::string& json,
226 const std::string matches[3],
227 const std::string& error_description);
229 // Verifies that |matches| and |expected_matches| agree on the first
230 // |num_expected_matches|, displaying an error message that includes
231 // |description| for any disagreement.
232 void CheckMatches(const std::string& description,
233 const size_t num_expected_matches,
234 const ExpectedMatch expected_matches[],
235 const ACMatches& matches);
237 void ResetFieldTrialList();
239 // Enable or disable the specified Omnibox field trial rule.
240 base::FieldTrial* CreateFieldTrial(const char* field_trial_rule,
241 bool enabled);
243 void ClearAllResults();
245 // See description above class for details of these fields.
246 TemplateURL* default_t_url_;
247 const base::string16 term1_;
248 GURL term1_url_;
249 TemplateURL* keyword_t_url_;
250 const base::string16 keyword_term_;
251 GURL keyword_url_;
253 content::TestBrowserThreadBundle thread_bundle_;
255 net::TestURLFetcherFactory test_factory_;
256 TestingProfile profile_;
257 scoped_ptr<ChromeAutocompleteProviderClient> client_;
258 scoped_refptr<SearchProviderForTest> provider_;
260 // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
261 base::RunLoop* run_loop_;
263 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
266 // static
267 const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
268 const SearchProviderTest::ExpectedMatch
269 SearchProviderTest::kEmptyExpectedMatch = { kNotApplicable, false };
271 void SearchProviderTest::SetUp() {
272 // Make sure that fetchers are automatically ungregistered upon destruction.
273 test_factory_.set_remove_fetcher_on_delete(true);
275 // We need both the history service and template url model loaded.
276 ASSERT_TRUE(profile_.CreateHistoryService(true, false));
277 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
278 &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
280 client_.reset(new ChromeAutocompleteProviderClient(&profile_));
282 TemplateURLService* turl_model =
283 TemplateURLServiceFactory::GetForProfile(&profile_);
285 turl_model->Load();
287 // Reset the default TemplateURL.
288 TemplateURLData data;
289 data.SetShortName(ASCIIToUTF16("t"));
290 data.SetURL("http://defaultturl/{searchTerms}");
291 data.suggestions_url = "http://defaultturl2/{searchTerms}";
292 data.instant_url = "http://does/not/exist?strk=1";
293 data.search_terms_replacement_key = "strk";
294 default_t_url_ = new TemplateURL(data);
295 turl_model->Add(default_t_url_);
296 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
297 TemplateURLID default_provider_id = default_t_url_->id();
298 ASSERT_NE(0, default_provider_id);
300 // Add url1, with search term term1_.
301 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
303 // Create another TemplateURL.
304 data.SetShortName(ASCIIToUTF16("k"));
305 data.SetKeyword(ASCIIToUTF16("k"));
306 data.SetURL("http://keyword/{searchTerms}");
307 data.suggestions_url = "http://suggest_keyword/{searchTerms}";
308 keyword_t_url_ = new TemplateURL(data);
309 turl_model->Add(keyword_t_url_);
310 ASSERT_NE(0, keyword_t_url_->id());
312 // Add a page and search term for keyword_t_url_.
313 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
315 // Keywords are updated by the InMemoryHistoryBackend only after the message
316 // has been processed on the history thread. Block until history processes all
317 // requests to ensure the InMemoryDatabase is the state we expect it.
318 profile_.BlockUntilHistoryProcessesPendingRequests();
320 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
321 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
323 provider_ = new SearchProviderForTest(client_.get(), this, &profile_);
324 OmniboxFieldTrial::kDefaultMinimumTimeBetweenSuggestQueriesMs = 0;
327 void SearchProviderTest::TearDown() {
328 base::RunLoop().RunUntilIdle();
330 // Shutdown the provider before the profile.
331 provider_ = NULL;
334 void SearchProviderTest::RunTest(TestData* cases,
335 int num_cases,
336 bool prefer_keyword) {
337 ACMatches matches;
338 for (int i = 0; i < num_cases; ++i) {
339 AutocompleteInput input(cases[i].input, base::string16::npos, std::string(),
340 GURL(), metrics::OmniboxEventProto::INVALID_SPEC,
341 false, prefer_keyword, true, true, false,
342 ChromeAutocompleteSchemeClassifier(&profile_));
343 provider_->Start(input, false);
344 matches = provider_->matches();
345 SCOPED_TRACE(
346 ASCIIToUTF16("Input was: ") +
347 cases[i].input +
348 ASCIIToUTF16("; prefer_keyword was: ") +
349 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false")));
350 EXPECT_EQ(cases[i].num_results, matches.size());
351 if (matches.size() == cases[i].num_results) {
352 for (size_t j = 0; j < cases[i].num_results; ++j) {
353 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url);
354 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type);
355 EXPECT_EQ(cases[i].output[j].fill_into_edit,
356 matches[j].fill_into_edit);
357 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
358 matches[j].allowed_to_be_default_match);
364 void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
365 if (run_loop_ && provider_->done()) {
366 run_loop_->Quit();
367 run_loop_ = NULL;
371 void SearchProviderTest::RunTillProviderDone() {
372 if (provider_->done())
373 return;
375 base::RunLoop run_loop;
376 run_loop_ = &run_loop;
377 run_loop.Run();
380 void SearchProviderTest::QueryForInput(const base::string16& text,
381 bool prevent_inline_autocomplete,
382 bool prefer_keyword) {
383 // Start a query.
384 AutocompleteInput input(text, base::string16::npos, std::string(), GURL(),
385 metrics::OmniboxEventProto::INVALID_SPEC,
386 prevent_inline_autocomplete, prefer_keyword, true,
387 true, false,
388 ChromeAutocompleteSchemeClassifier(&profile_));
389 provider_->Start(input, false);
391 // RunUntilIdle so that the task scheduled by SearchProvider to create the
392 // URLFetchers runs.
393 base::RunLoop().RunUntilIdle();
396 void SearchProviderTest::QueryForInputAndSetWYTMatch(
397 const base::string16& text,
398 AutocompleteMatch* wyt_match) {
399 QueryForInput(text, false, false);
400 profile_.BlockUntilHistoryProcessesPendingRequests();
401 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
402 if (!wyt_match)
403 return;
404 ASSERT_GE(provider_->matches().size(), 1u);
405 EXPECT_TRUE(FindMatchWithDestination(
406 GURL(default_t_url_->url_ref().ReplaceSearchTerms(
407 TemplateURLRef::SearchTermsArgs(base::CollapseWhitespace(
408 text, false)),
409 TemplateURLServiceFactory::GetForProfile(
410 &profile_)->search_terms_data())),
411 wyt_match));
414 void SearchProviderTest::QueryForInputAndWaitForFetcherResponses(
415 const base::string16& text,
416 const bool prefer_keyword,
417 const std::string& default_fetcher_response,
418 const std::string& keyword_fetcher_response) {
419 QueryForInput(text, false, prefer_keyword);
420 if (!default_fetcher_response.empty()) {
421 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
422 SearchProvider::kDefaultProviderURLFetcherID);
423 ASSERT_TRUE(fetcher);
424 fetcher->set_response_code(200);
425 fetcher->SetResponseString(default_fetcher_response);
426 fetcher->delegate()->OnURLFetchComplete(fetcher);
428 if (!keyword_fetcher_response.empty()) {
429 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
430 SearchProvider::kKeywordProviderURLFetcherID);
431 ASSERT_TRUE(keyword_fetcher);
432 keyword_fetcher->set_response_code(200);
433 keyword_fetcher->SetResponseString(keyword_fetcher_response);
434 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
436 RunTillProviderDone();
439 GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
440 base::string16 term,
441 int visit_count) {
442 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
443 &profile_, ServiceAccessType::EXPLICIT_ACCESS);
444 GURL search(t_url->url_ref().ReplaceSearchTerms(
445 TemplateURLRef::SearchTermsArgs(term),
446 TemplateURLServiceFactory::GetForProfile(
447 &profile_)->search_terms_data()));
448 static base::Time last_added_time;
449 last_added_time = std::max(base::Time::Now(),
450 last_added_time + base::TimeDelta::FromMicroseconds(1));
451 history->AddPageWithDetails(search, base::string16(), visit_count, visit_count,
452 last_added_time, false, history::SOURCE_BROWSED);
453 history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
454 return search;
457 bool SearchProviderTest::FindMatchWithContents(const base::string16& contents,
458 AutocompleteMatch* match) {
459 for (ACMatches::const_iterator i = provider_->matches().begin();
460 i != provider_->matches().end(); ++i) {
461 if (i->contents == contents) {
462 *match = *i;
463 return true;
466 return false;
469 bool SearchProviderTest::FindMatchWithDestination(const GURL& url,
470 AutocompleteMatch* match) {
471 for (ACMatches::const_iterator i = provider_->matches().begin();
472 i != provider_->matches().end(); ++i) {
473 if (i->destination_url == url) {
474 *match = *i;
475 return true;
478 return false;
481 void SearchProviderTest::FinishDefaultSuggestQuery() {
482 net::TestURLFetcher* default_fetcher =
483 test_factory_.GetFetcherByID(
484 SearchProvider::kDefaultProviderURLFetcherID);
485 ASSERT_TRUE(default_fetcher);
487 // Tell the SearchProvider the default suggest query is done.
488 default_fetcher->set_response_code(200);
489 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
492 void SearchProviderTest::ForcedQueryTestHelper(
493 const std::string& input,
494 const std::string& json,
495 const std::string expected_matches[3],
496 const std::string& error_description) {
497 // Send the query twice in order to have a synchronous pass after the first
498 // response is received. This is necessary because SearchProvider doesn't
499 // allow an asynchronous response to change the default match.
500 for (size_t i = 0; i < 2; ++i) {
501 QueryForInputAndWaitForFetcherResponses(
502 ASCIIToUTF16(input), false, json, std::string());
505 const ACMatches& matches = provider_->matches();
506 ASSERT_LE(matches.size(), 3u);
507 size_t i = 0;
508 // Ensure that the returned matches equal the expectations.
509 for (; i < matches.size(); ++i) {
510 EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) <<
511 error_description;
513 // Ensure that no expected matches are missing.
514 for (; i < 3u; ++i) {
515 EXPECT_EQ(std::string(), expected_matches[i]) <<
516 "Case #" << i << ": " << error_description;
520 void SearchProviderTest::CheckMatches(const std::string& description,
521 const size_t num_expected_matches,
522 const ExpectedMatch expected_matches[],
523 const ACMatches& matches) {
524 ASSERT_FALSE(matches.empty());
525 ASSERT_LE(matches.size(), num_expected_matches);
526 size_t i = 0;
527 SCOPED_TRACE(description);
528 // Ensure that the returned matches equal the expectations.
529 for (; i < matches.size(); ++i) {
530 SCOPED_TRACE(" Case # " + base::SizeTToString(i));
531 EXPECT_EQ(ASCIIToUTF16(expected_matches[i].contents), matches[i].contents);
532 EXPECT_EQ(expected_matches[i].allowed_to_be_default_match,
533 matches[i].allowed_to_be_default_match);
535 // Ensure that no expected matches are missing.
536 for (; i < num_expected_matches; ++i) {
537 SCOPED_TRACE(" Case # " + base::SizeTToString(i));
538 EXPECT_EQ(kNotApplicable, expected_matches[i].contents);
542 void SearchProviderTest::ResetFieldTrialList() {
543 // Destroy the existing FieldTrialList before creating a new one to avoid
544 // a DCHECK.
545 field_trial_list_.reset();
546 field_trial_list_.reset(new base::FieldTrialList(
547 new metrics::SHA1EntropyProvider("foo")));
548 variations::testing::ClearAllVariationParams();
549 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
550 "AutocompleteDynamicTrial_0", "DefaultGroup");
551 trial->group();
553 base::FieldTrial* SearchProviderTest::CreateFieldTrial(
554 const char* field_trial_rule,
555 bool enabled) {
556 std::map<std::string, std::string> params;
557 params[std::string(field_trial_rule)] = enabled ?
558 "true" : "false";
559 variations::AssociateVariationParams(
560 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params);
561 return base::FieldTrialList::CreateFieldTrial(
562 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
565 void SearchProviderTest::ClearAllResults() {
566 provider_->ClearAllResults();
569 // Actual Tests ---------------------------------------------------------------
571 // Make sure we query history for the default provider and a URLFetcher is
572 // created for the default provider suggest results.
573 TEST_F(SearchProviderTest, QueryDefaultProvider) {
574 base::string16 term = term1_.substr(0, term1_.length() - 1);
575 QueryForInput(term, false, false);
577 // Make sure the default providers suggest service was queried.
578 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
579 SearchProvider::kDefaultProviderURLFetcherID);
580 ASSERT_TRUE(fetcher);
582 // And the URL matches what we expected.
583 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
584 TemplateURLRef::SearchTermsArgs(term),
585 TemplateURLServiceFactory::GetForProfile(
586 &profile_)->search_terms_data()));
587 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
589 // Tell the SearchProvider the suggest query is done.
590 fetcher->set_response_code(200);
591 fetcher->delegate()->OnURLFetchComplete(fetcher);
592 fetcher = NULL;
594 // Run till the history results complete.
595 RunTillProviderDone();
597 // The SearchProvider is done. Make sure it has a result for the history
598 // term term1.
599 AutocompleteMatch term1_match;
600 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match));
601 // Term1 should not have a description, it's set later.
602 EXPECT_TRUE(term1_match.description.empty());
604 AutocompleteMatch wyt_match;
605 EXPECT_TRUE(FindMatchWithDestination(
606 GURL(default_t_url_->url_ref().ReplaceSearchTerms(
607 TemplateURLRef::SearchTermsArgs(term),
608 TemplateURLServiceFactory::GetForProfile(
609 &profile_)->search_terms_data())),
610 &wyt_match));
611 EXPECT_TRUE(wyt_match.description.empty());
613 // The match for term1 should be more relevant than the what you typed match.
614 EXPECT_GT(term1_match.relevance, wyt_match.relevance);
615 // This longer match should be inlineable.
616 EXPECT_TRUE(term1_match.allowed_to_be_default_match);
617 // The what you typed match should be too, of course.
618 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
621 // Make sure we get a query-what-you-typed result from the default search
622 // provider even if the default search provider's keyword is renamed in the
623 // middle of processing the query.
624 TEST_F(SearchProviderTest, HasQueryWhatYouTypedIfDefaultKeywordChanges) {
625 base::string16 query = ASCIIToUTF16("query");
626 QueryForInput(query, false, false);
628 // Make sure the default provider's suggest service was queried.
629 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
630 SearchProvider::kDefaultProviderURLFetcherID);
631 ASSERT_TRUE(fetcher);
633 // Look up the TemplateURL for the keyword and modify its keyword.
634 TemplateURLService* template_url_service =
635 TemplateURLServiceFactory::GetForProfile(&profile_);
636 TemplateURL* template_url =
637 template_url_service->GetTemplateURLForKeyword(default_t_url_->keyword());
638 EXPECT_TRUE(template_url);
639 template_url_service->ResetTemplateURL(
640 template_url, template_url->short_name(),
641 ASCIIToUTF16("new_keyword_asdf"), template_url->url());
643 // In resetting the default provider, the fetcher should've been canceled.
644 fetcher = test_factory_.GetFetcherByID(
645 SearchProvider::kDefaultProviderURLFetcherID);
646 EXPECT_TRUE(!fetcher);
647 RunTillProviderDone();
649 // Makes sure the query-what-you-typed match is there.
650 AutocompleteMatch wyt_match;
651 EXPECT_TRUE(FindMatchWithDestination(
652 GURL(default_t_url_->url_ref().ReplaceSearchTerms(
653 TemplateURLRef::SearchTermsArgs(query),
654 TemplateURLServiceFactory::GetForProfile(
655 &profile_)->search_terms_data())),
656 &wyt_match));
657 EXPECT_TRUE(wyt_match.description.empty());
658 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
661 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
662 base::string16 term = term1_.substr(0, term1_.length() - 1);
663 QueryForInput(term, true, false);
665 ASSERT_FALSE(provider_->matches().empty());
666 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
667 provider_->matches()[0].type);
668 EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match);
671 // Issues a query that matches the registered keyword and makes sure history
672 // is queried as well as URLFetchers getting created.
673 TEST_F(SearchProviderTest, QueryKeywordProvider) {
674 base::string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
675 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
676 false,
677 false);
679 // Make sure the default providers suggest service was queried.
680 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
681 SearchProvider::kDefaultProviderURLFetcherID);
682 ASSERT_TRUE(default_fetcher);
684 // Tell the SearchProvider the default suggest query is done.
685 default_fetcher->set_response_code(200);
686 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
687 default_fetcher = NULL;
689 // Make sure the keyword providers suggest service was queried.
690 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
691 SearchProvider::kKeywordProviderURLFetcherID);
692 ASSERT_TRUE(keyword_fetcher);
694 // And the URL matches what we expected.
695 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
696 TemplateURLRef::SearchTermsArgs(term),
697 TemplateURLServiceFactory::GetForProfile(
698 &profile_)->search_terms_data()));
699 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
701 // Tell the SearchProvider the keyword suggest query is done.
702 keyword_fetcher->set_response_code(200);
703 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
704 keyword_fetcher = NULL;
706 // Run till the history results complete.
707 RunTillProviderDone();
709 // The SearchProvider is done. Make sure it has a result for the history
710 // term keyword.
711 AutocompleteMatch match;
712 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match));
714 // The match should have an associated keyword.
715 EXPECT_FALSE(match.keyword.empty());
717 // The fill into edit should contain the keyword.
718 EXPECT_EQ(keyword_t_url_->keyword() + base::char16(' ') + keyword_term_,
719 match.fill_into_edit);
722 TEST_F(SearchProviderTest, SendDataToSuggestAtAppropriateTimes) {
723 struct {
724 std::string input;
725 const bool expect_to_send_to_default_provider;
726 } cases[] = {
727 // None of the following input strings should be sent to the default
728 // suggest server because they may contain potentially private data.
729 { "username:password", false },
730 { "User:f", false },
731 { "http://username:password", false },
732 { "https://username:password", false },
733 { "username:password@hostname", false },
734 { "http://username:password@hostname/", false },
735 { "file://filename", false },
736 { "data://data", false },
737 { "unknownscheme:anything", false },
738 { "http://hostname/?query=q", false },
739 { "http://hostname/path#ref", false },
740 { "http://hostname/path #ref", false },
741 { "https://hostname/path", false },
742 // For all of the following input strings, it doesn't make much difference
743 // if we allow them to be sent to the default provider or not. The strings
744 // need to be in this list of test cases however so that they are tested
745 // against the keyword provider and verified that they are allowed to be
746 // sent to it.
747 { "User:", false },
748 { "User::", false },
749 { "User:!", false },
750 // All of the following input strings should be sent to the default suggest
751 // server because they should not get caught by the private data checks.
752 { "User", true },
753 { "query", true },
754 { "query with spaces", true },
755 { "http://hostname", true },
756 { "http://hostname/path", true },
757 { "http://hostname #ref", true },
758 { "www.hostname.com #ref", true },
759 { "https://hostname", true },
760 { "#hashtag", true },
761 { "foo https://hostname/path", true },
764 for (size_t i = 0; i < arraysize(cases); ++i) {
765 SCOPED_TRACE("for input=" + cases[i].input);
766 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
767 // Make sure the default provider's suggest service was or was not queried
768 // as appropriate.
769 EXPECT_EQ(
770 cases[i].expect_to_send_to_default_provider,
771 test_factory_.GetFetcherByID(
772 SearchProvider::kDefaultProviderURLFetcherID) != NULL);
773 // Send the same input with an explicitly invoked keyword. In all cases,
774 // it's okay to send the request to the keyword suggest server.
775 QueryForInput(ASCIIToUTF16("k ") + ASCIIToUTF16(cases[i].input), false,
776 false);
777 EXPECT_TRUE(test_factory_.GetFetcherByID(
778 SearchProvider::kKeywordProviderURLFetcherID) != NULL);
782 TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
783 GURL url = AddSearchToHistory(default_t_url_,
784 ASCIIToUTF16("docs.google.com"), 1);
786 // Add the term as a url.
787 HistoryServiceFactory::GetForProfile(&profile_,
788 ServiceAccessType::EXPLICIT_ACCESS)
789 ->AddPageWithDetails(GURL("http://docs.google.com"), base::string16(), 1,
790 1, base::Time::Now(), false,
791 history::SOURCE_BROWSED);
792 profile_.BlockUntilHistoryProcessesPendingRequests();
794 AutocompleteMatch wyt_match;
795 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
796 &wyt_match));
798 // There should be two matches, one for what you typed, the other for
799 // 'docs.google.com'. The search term should have a lower priority than the
800 // what you typed match.
801 ASSERT_EQ(2u, provider_->matches().size());
802 AutocompleteMatch term_match;
803 EXPECT_TRUE(FindMatchWithDestination(url, &term_match));
804 EXPECT_GT(wyt_match.relevance, term_match.relevance);
805 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
806 EXPECT_TRUE(term_match.allowed_to_be_default_match);
809 TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) {
810 const std::string kEmptyMatch;
811 struct {
812 const std::string json;
813 const std::string matches_in_default_mode[3];
814 const std::string matches_in_forced_query_mode[3];
815 } cases[] = {
816 // Without suggested relevance scores.
817 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
818 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]",
819 { "a", "a1.com", "a2" },
820 { "a", "a2", kEmptyMatch } },
822 // With suggested relevance scores in a situation where navsuggest would
823 // go second.
824 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
825 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
826 "\"google:suggestrelevance\":[1250, 1200]}]",
827 { "a", "a1.com", "a2" },
828 { "a", "a2", kEmptyMatch } },
830 // With suggested relevance scores in a situation where navsuggest
831 // would go first.
832 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
833 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
834 "\"google:suggestrelevance\":[1350, 1250]}]",
835 { "a1.com", "a", "a2" },
836 { "a", "a2", kEmptyMatch } },
838 // With suggested relevance scores in a situation where navsuggest
839 // would go first only because verbatim has been demoted.
840 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
841 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
842 "\"google:suggestrelevance\":[1450, 1400],"
843 "\"google:verbatimrelevance\":1350}]",
844 { "a1.com", "a2", "a" },
845 { "a2", "a", kEmptyMatch } },
848 for (size_t i = 0; i < arraysize(cases); ++i) {
849 ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode,
850 "regular input with json=" + cases[i].json);
851 ForcedQueryTestHelper("?a", cases[i].json,
852 cases[i].matches_in_forced_query_mode,
853 "forced query input with json=" + cases[i].json);
857 // A multiword search with one visit should not autocomplete until multiple
858 // words are typed.
859 TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
860 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
861 1));
862 profile_.BlockUntilHistoryProcessesPendingRequests();
864 AutocompleteMatch wyt_match;
865 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"),
866 &wyt_match));
867 ASSERT_EQ(2u, provider_->matches().size());
868 AutocompleteMatch term_match;
869 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
870 EXPECT_GT(wyt_match.relevance, term_match.relevance);
871 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
872 EXPECT_TRUE(term_match.allowed_to_be_default_match);
874 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"),
875 &wyt_match));
876 ASSERT_EQ(2u, provider_->matches().size());
877 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
878 EXPECT_GT(term_match.relevance, wyt_match.relevance);
879 EXPECT_TRUE(term_match.allowed_to_be_default_match);
880 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
883 // A multiword search with more than one visit should autocomplete immediately.
884 TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
885 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
886 2));
887 profile_.BlockUntilHistoryProcessesPendingRequests();
889 AutocompleteMatch wyt_match;
890 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"),
891 &wyt_match));
892 ASSERT_EQ(2u, provider_->matches().size());
893 AutocompleteMatch term_match;
894 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
895 EXPECT_GT(term_match.relevance, wyt_match.relevance);
896 EXPECT_TRUE(term_match.allowed_to_be_default_match);
897 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
900 // Autocompletion should work at a word boundary after a space, and should
901 // offer a suggestion for the trimmed search query.
902 TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
903 AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches "), 2);
904 GURL suggested_url(default_t_url_->url_ref().ReplaceSearchTerms(
905 TemplateURLRef::SearchTermsArgs(ASCIIToUTF16("two searches")),
906 TemplateURLServiceFactory::GetForProfile(
907 &profile_)->search_terms_data()));
908 profile_.BlockUntilHistoryProcessesPendingRequests();
910 AutocompleteMatch wyt_match;
911 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "),
912 &wyt_match));
913 ASSERT_EQ(2u, provider_->matches().size());
914 AutocompleteMatch term_match;
915 EXPECT_TRUE(FindMatchWithDestination(suggested_url, &term_match));
916 EXPECT_GT(term_match.relevance, wyt_match.relevance);
917 EXPECT_TRUE(term_match.allowed_to_be_default_match);
918 EXPECT_EQ(ASCIIToUTF16("searches"), term_match.inline_autocompletion);
919 EXPECT_EQ(ASCIIToUTF16("two searches"), term_match.fill_into_edit);
920 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
923 // Newer multiword searches should score more highly than older ones.
924 TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
925 GURL term_url_a(AddSearchToHistory(default_t_url_,
926 ASCIIToUTF16("three searches aaa"), 1));
927 GURL term_url_b(AddSearchToHistory(default_t_url_,
928 ASCIIToUTF16("three searches bbb"), 1));
929 profile_.BlockUntilHistoryProcessesPendingRequests();
931 AutocompleteMatch wyt_match;
932 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"),
933 &wyt_match));
934 ASSERT_EQ(3u, provider_->matches().size());
935 AutocompleteMatch term_match_a;
936 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
937 AutocompleteMatch term_match_b;
938 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
939 EXPECT_GT(term_match_b.relevance, term_match_a.relevance);
940 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
941 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
942 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
943 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
946 // If ScoreHistoryResults doesn't properly clear its output vector it can skip
947 // scoring the actual results and just return results from a previous run.
948 TEST_F(SearchProviderTest, ResetResultsBetweenRuns) {
949 GURL term_url_a(AddSearchToHistory(default_t_url_,
950 ASCIIToUTF16("games"), 1));
951 GURL term_url_b(AddSearchToHistory(default_t_url_,
952 ASCIIToUTF16("gangnam style"), 1));
953 GURL term_url_c(AddSearchToHistory(default_t_url_,
954 ASCIIToUTF16("gundam"), 1));
955 profile_.BlockUntilHistoryProcessesPendingRequests();
957 AutocompleteMatch wyt_match;
958 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
959 &wyt_match));
960 ASSERT_EQ(1u, provider_->matches().size());
962 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("g"),
963 &wyt_match));
964 ASSERT_EQ(4u, provider_->matches().size());
966 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("ga"),
967 &wyt_match));
968 ASSERT_EQ(3u, provider_->matches().size());
970 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("gan"),
971 &wyt_match));
972 ASSERT_EQ(2u, provider_->matches().size());
974 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("gans"),
975 &wyt_match));
976 ASSERT_EQ(1u, provider_->matches().size());
979 // An autocompleted multiword search should not be replaced by a different
980 // autocompletion while the user is still typing a valid prefix unless the
981 // user has typed the prefix as a query before.
982 TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
983 GURL term_url_a(AddSearchToHistory(default_t_url_,
984 ASCIIToUTF16("four searches aaa"), 3));
985 GURL term_url_b(AddSearchToHistory(default_t_url_,
986 ASCIIToUTF16("four searches bbb"), 1));
987 GURL term_url_c(AddSearchToHistory(default_t_url_,
988 ASCIIToUTF16("four searches"), 1));
989 profile_.BlockUntilHistoryProcessesPendingRequests();
991 AutocompleteMatch wyt_match;
992 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
993 &wyt_match));
994 ASSERT_EQ(4u, provider_->matches().size());
995 AutocompleteMatch term_match_a;
996 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
997 AutocompleteMatch term_match_b;
998 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
999 AutocompleteMatch term_match_c;
1000 EXPECT_TRUE(FindMatchWithDestination(term_url_c, &term_match_c));
1001 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
1002 // We don't care about the relative order of b and c.
1003 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
1004 EXPECT_GT(wyt_match.relevance, term_match_c.relevance);
1005 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
1006 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
1007 EXPECT_TRUE(term_match_c.allowed_to_be_default_match);
1008 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
1010 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
1011 &wyt_match));
1012 ASSERT_EQ(4u, provider_->matches().size());
1013 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
1014 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
1015 EXPECT_TRUE(FindMatchWithDestination(term_url_c, &term_match_c));
1016 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
1017 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
1018 EXPECT_GT(wyt_match.relevance, term_match_c.relevance);
1019 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
1020 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
1021 EXPECT_TRUE(term_match_c.allowed_to_be_default_match);
1022 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
1024 // For the exact previously-issued query, the what-you-typed match should win.
1025 ASSERT_NO_FATAL_FAILURE(
1026 QueryForInputAndSetWYTMatch(ASCIIToUTF16("four searches"), &wyt_match));
1027 ASSERT_EQ(3u, provider_->matches().size());
1028 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
1029 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
1030 EXPECT_GT(wyt_match.relevance, term_match_a.relevance);
1031 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
1032 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
1033 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
1034 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
1037 // Non-completable multiword searches should not crowd out single-word searches.
1038 TEST_F(SearchProviderTest, DontCrowdOutSingleWords) {
1039 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1));
1040 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1);
1041 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1);
1042 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1);
1043 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1);
1044 profile_.BlockUntilHistoryProcessesPendingRequests();
1046 AutocompleteMatch wyt_match;
1047 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"),
1048 &wyt_match));
1049 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size());
1050 AutocompleteMatch term_match;
1051 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
1052 EXPECT_GT(term_match.relevance, wyt_match.relevance);
1053 EXPECT_TRUE(term_match.allowed_to_be_default_match);
1054 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
1057 // Inline autocomplete matches regardless of case differences from the input.
1058 TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
1059 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
1060 profile_.BlockUntilHistoryProcessesPendingRequests();
1062 AutocompleteMatch wyt_match;
1063 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
1064 &wyt_match));
1065 ASSERT_EQ(2u, provider_->matches().size());
1066 AutocompleteMatch term_match;
1067 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
1068 EXPECT_GT(term_match.relevance, wyt_match.relevance);
1069 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit);
1070 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion);
1071 EXPECT_TRUE(term_match.allowed_to_be_default_match);
1072 // Make sure the case doesn't affect the highlighting.
1073 // (SearchProvider intentionally marks the new text as MATCH; that's why
1074 // the tests below look backwards.)
1075 ASSERT_EQ(2U, term_match.contents_class.size());
1076 EXPECT_EQ(0U, term_match.contents_class[0].offset);
1077 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::NONE,
1078 term_match.contents_class[0].style);
1079 EXPECT_EQ(1U, term_match.contents_class[1].offset);
1080 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::MATCH,
1081 term_match.contents_class[1].style);
1084 // Verifies AutocompleteControllers return results (including keyword
1085 // results) in the right order and set descriptions for them correctly.
1086 TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
1087 // Add an entry that corresponds to a keyword search with 'term2'.
1088 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
1089 profile_.BlockUntilHistoryProcessesPendingRequests();
1091 AutocompleteController controller(
1092 make_scoped_ptr(new ChromeAutocompleteProviderClient(&profile_)), nullptr,
1093 AutocompleteProvider::TYPE_SEARCH);
1094 controller.Start(AutocompleteInput(
1095 ASCIIToUTF16("k t"), base::string16::npos, std::string(), GURL(),
1096 metrics::OmniboxEventProto::INVALID_SPEC, false, false, true, true, false,
1097 ChromeAutocompleteSchemeClassifier(&profile_)));
1098 const AutocompleteResult& result = controller.result();
1100 // There should be three matches, one for the keyword history, one for
1101 // keyword provider's what-you-typed, and one for the default provider's
1102 // what you typed, in that order.
1103 ASSERT_EQ(3u, result.size());
1104 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type);
1105 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1106 result.match_at(1).type);
1107 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1108 result.match_at(2).type);
1109 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance);
1110 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
1111 EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
1112 EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
1113 EXPECT_FALSE(result.match_at(2).allowed_to_be_default_match);
1115 // The two keyword results should come with the keyword we expect.
1116 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
1117 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword);
1118 // The default provider has a different keyword. (We don't explicitly
1119 // set it during this test, so all we do is assert that it's different.)
1120 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword);
1122 // The top result will always have a description. The third result,
1123 // coming from a different provider than the first two, should also.
1124 // Whether the second result has one doesn't matter much. (If it was
1125 // missing, people would infer that it's the same search provider as
1126 // the one above it.)
1127 EXPECT_FALSE(result.match_at(0).description.empty());
1128 EXPECT_FALSE(result.match_at(2).description.empty());
1129 EXPECT_NE(result.match_at(0).description, result.match_at(2).description);
1132 TEST_F(SearchProviderTest, KeywordVerbatim) {
1133 TestData cases[] = {
1134 // Test a simple keyword input.
1135 { ASCIIToUTF16("k foo"), 2,
1136 { ResultInfo(GURL("http://keyword/foo"),
1137 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1138 true,
1139 ASCIIToUTF16("k foo")),
1140 ResultInfo(GURL("http://defaultturl/k%20foo"),
1141 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1142 false,
1143 ASCIIToUTF16("k foo") ) } },
1145 // Make sure extra whitespace after the keyword doesn't change the
1146 // keyword verbatim query. Also verify that interior consecutive
1147 // whitespace gets trimmed.
1148 { ASCIIToUTF16("k foo"), 2,
1149 { ResultInfo(GURL("http://keyword/foo"),
1150 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1151 true,
1152 ASCIIToUTF16("k foo")),
1153 ResultInfo(GURL("http://defaultturl/k%20foo"),
1154 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1155 false,
1156 ASCIIToUTF16("k foo")) } },
1157 // Leading whitespace should be stripped before SearchProvider gets the
1158 // input; hence there are no tests here about how it handles those inputs.
1160 // Verify that interior consecutive whitespace gets trimmed in either case.
1161 { ASCIIToUTF16("k foo bar"), 2,
1162 { ResultInfo(GURL("http://keyword/foo%20bar"),
1163 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1164 true,
1165 ASCIIToUTF16("k foo bar")),
1166 ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
1167 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1168 false,
1169 ASCIIToUTF16("k foo bar")) } },
1171 // Verify that trailing whitespace gets trimmed.
1172 { ASCIIToUTF16("k foo bar "), 2,
1173 { ResultInfo(GURL("http://keyword/foo%20bar"),
1174 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1175 true,
1176 ASCIIToUTF16("k foo bar")),
1177 ResultInfo(GURL("http://defaultturl/k%20foo%20bar"),
1178 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1179 false,
1180 ASCIIToUTF16("k foo bar")) } },
1182 // Keywords can be prefixed by certain things that should get ignored
1183 // when constructing the keyword match.
1184 { ASCIIToUTF16("www.k foo"), 2,
1185 { ResultInfo(GURL("http://keyword/foo"),
1186 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1187 true,
1188 ASCIIToUTF16("k foo")),
1189 ResultInfo(GURL("http://defaultturl/www.k%20foo"),
1190 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1191 false,
1192 ASCIIToUTF16("www.k foo")) } },
1193 { ASCIIToUTF16("http://k foo"), 2,
1194 { ResultInfo(GURL("http://keyword/foo"),
1195 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1196 true,
1197 ASCIIToUTF16("k foo")),
1198 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
1199 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1200 false,
1201 ASCIIToUTF16("http://k foo")) } },
1202 { ASCIIToUTF16("http://www.k foo"), 2,
1203 { ResultInfo(GURL("http://keyword/foo"),
1204 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1205 true,
1206 ASCIIToUTF16("k foo")),
1207 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
1208 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1209 false,
1210 ASCIIToUTF16("http://www.k foo")) } },
1212 // A keyword with no remaining input shouldn't get a keyword
1213 // verbatim match.
1214 { ASCIIToUTF16("k"), 1,
1215 { ResultInfo(GURL("http://defaultturl/k"),
1216 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1217 true,
1218 ASCIIToUTF16("k")) } },
1219 // Ditto. Trailing whitespace shouldn't make a difference.
1220 { ASCIIToUTF16("k "), 1,
1221 { ResultInfo(GURL("http://defaultturl/k"),
1222 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1223 true,
1224 ASCIIToUTF16("k")) } }
1226 // The fact that verbatim queries to keyword are handled by KeywordProvider
1227 // not SearchProvider is tested in
1228 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc.
1231 // Test not in keyword mode.
1232 RunTest(cases, arraysize(cases), false);
1234 // Test in keyword mode. (Both modes should give the same result.)
1235 RunTest(cases, arraysize(cases), true);
1238 // Ensures command-line flags are reflected in the URLs the search provider
1239 // generates.
1240 TEST_F(SearchProviderTest, CommandLineOverrides) {
1241 TemplateURLService* turl_model =
1242 TemplateURLServiceFactory::GetForProfile(&profile_);
1244 TemplateURLData data;
1245 data.SetShortName(ASCIIToUTF16("default"));
1246 data.SetKeyword(data.short_name());
1247 data.SetURL("{google:baseURL}{searchTerms}");
1248 default_t_url_ = new TemplateURL(data);
1249 turl_model->Add(default_t_url_);
1250 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
1252 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1253 switches::kGoogleBaseURL, "http://www.bar.com/");
1254 base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
1255 switches::kExtraSearchQueryParams, "a=b");
1257 TestData cases[] = {
1258 { ASCIIToUTF16("k a"), 2,
1259 { ResultInfo(GURL("http://keyword/a"),
1260 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
1261 true,
1262 ASCIIToUTF16("k a")),
1263 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
1264 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
1265 false,
1266 ASCIIToUTF16("k a")) } },
1269 RunTest(cases, arraysize(cases), false);
1272 // Verifies Navsuggest results don't set a TemplateURL, which Instant relies on.
1273 // Also verifies that just the *first* navigational result is listed as a match
1274 // if suggested relevance scores were not sent.
1275 TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) {
1276 QueryForInputAndWaitForFetcherResponses(
1277 ASCIIToUTF16("a.c"), false,
1278 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[],"
1279 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1280 std::string());
1282 // Make sure the only match is 'a.com' and it doesn't have a template_url.
1283 AutocompleteMatch nav_match;
1284 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match));
1285 EXPECT_TRUE(nav_match.keyword.empty());
1286 EXPECT_FALSE(nav_match.allowed_to_be_default_match);
1287 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match));
1290 // Verifies that the most relevant suggest results are added properly.
1291 TEST_F(SearchProviderTest, SuggestRelevance) {
1292 QueryForInputAndWaitForFetcherResponses(
1293 ASCIIToUTF16("a"), false, "[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]",
1294 std::string());
1296 // Check the expected verbatim and (first 3) suggestions' relative relevances.
1297 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4;
1298 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
1299 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1));
1300 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2));
1301 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3));
1302 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4));
1303 EXPECT_GT(verbatim.relevance, match_a1.relevance);
1304 EXPECT_GT(match_a1.relevance, match_a2.relevance);
1305 EXPECT_GT(match_a2.relevance, match_a3.relevance);
1306 EXPECT_TRUE(verbatim.allowed_to_be_default_match);
1307 EXPECT_FALSE(match_a1.allowed_to_be_default_match);
1308 EXPECT_FALSE(match_a2.allowed_to_be_default_match);
1309 EXPECT_FALSE(match_a3.allowed_to_be_default_match);
1312 // Verifies that the default provider abandons suggested relevance scores
1313 // when in keyword mode. This should happen regardless of whether the
1314 // keyword provider returns suggested relevance scores.
1315 TEST_F(SearchProviderTest, DefaultProviderNoSuggestRelevanceInKeywordMode) {
1316 struct {
1317 const std::string default_provider_json;
1318 const std::string keyword_provider_json;
1319 const std::string matches[5];
1320 } cases[] = {
1321 // First, try an input where the keyword provider does not deliver
1322 // suggested relevance scores.
1323 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1324 "{\"google:verbatimrelevance\":9700,"
1325 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1326 "\"google:suggestrelevance\":[9900, 9800]}]",
1327 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"]}]",
1328 { "a", "akeyword-query", "k a", "adefault.com", "k adefault-query" } },
1330 // Now try with keyword provider suggested relevance scores.
1331 { "[\"k a\",[\"k adefault-query\", \"adefault.com\"],[],[],"
1332 "{\"google:verbatimrelevance\":9700,"
1333 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
1334 "\"google:suggestrelevance\":[9900, 9800]}]",
1335 "[\"a\",[\"akeyword-query\"],[],[],{\"google:suggesttype\":[\"QUERY\"],"
1336 "\"google:verbatimrelevance\":9500,"
1337 "\"google:suggestrelevance\":[9600]}]",
1338 { "akeyword-query", "a", "k a", "adefault.com", "k adefault-query" } }
1341 for (size_t i = 0; i < arraysize(cases); ++i) {
1342 // Send the query twice in order to have a synchronous pass after the first
1343 // response is received. This is necessary because SearchProvider doesn't
1344 // allow an asynchronous response to change the default match.
1345 for (size_t j = 0; j < 2; ++j) {
1346 QueryForInputAndWaitForFetcherResponses(
1347 ASCIIToUTF16("k a"), true, cases[i].default_provider_json,
1348 cases[i].keyword_provider_json);
1351 SCOPED_TRACE(
1352 "for input with default_provider_json=" +
1353 cases[i].default_provider_json + " and keyword_provider_json=" +
1354 cases[i].keyword_provider_json);
1355 const ACMatches& matches = provider_->matches();
1356 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
1357 size_t j = 0;
1358 // Ensure that the returned matches equal the expectations.
1359 for (; j < matches.size(); ++j)
1360 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]), matches[j].contents);
1361 // Ensure that no expected matches are missing.
1362 for (; j < arraysize(cases[i].matches); ++j)
1363 EXPECT_EQ(std::string(), cases[i].matches[j]);
1367 // Verifies that suggest results with relevance scores are added
1368 // properly when using the default fetcher. When adding a new test
1369 // case to this test, please consider adding it to the tests in
1370 // KeywordFetcherSuggestRelevance below.
1371 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
1372 struct {
1373 const std::string json;
1374 const ExpectedMatch matches[6];
1375 const std::string inline_autocompletion;
1376 } cases[] = {
1377 // Ensure that suggestrelevance scores reorder matches.
1378 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1379 { { "a", true }, { "c", false }, { "b", false }, kEmptyExpectedMatch,
1380 kEmptyExpectedMatch, kEmptyExpectedMatch },
1381 std::string() },
1382 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1383 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1384 "\"google:suggestrelevance\":[1, 2]}]",
1385 { { "a", true }, { "c.com", false }, { "b.com", false },
1386 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1387 std::string() },
1389 // Without suggested relevance scores, we should only allow one
1390 // navsuggest result to be be displayed.
1391 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1392 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1393 { { "a", true }, { "b.com", false }, kEmptyExpectedMatch,
1394 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1395 std::string() },
1397 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1398 // Negative values will have no effect; the calculated value will be used.
1399 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1400 "\"google:suggestrelevance\":[9998]}]",
1401 { { "a", true}, { "a1", false }, kEmptyExpectedMatch,
1402 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1403 std::string() },
1404 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1405 "\"google:suggestrelevance\":[9999]}]",
1406 { { "a1", true }, { "a", true }, kEmptyExpectedMatch,
1407 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1408 "1" },
1409 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1410 "\"google:suggestrelevance\":[9999]}]",
1411 { { "a1", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1412 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1413 "1" },
1414 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1415 "\"google:suggestrelevance\":[9999]}]",
1416 { { "a1", true }, { "a", true }, kEmptyExpectedMatch,
1417 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1418 "1" },
1419 { "[\"a\",[\"http://a.com\"],[],[],"
1420 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1421 "\"google:verbatimrelevance\":9999,"
1422 "\"google:suggestrelevance\":[9998]}]",
1423 { { "a", true }, { "a.com", false }, kEmptyExpectedMatch,
1424 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1425 std::string() },
1426 { "[\"a\",[\"http://a.com\"],[],[],"
1427 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1428 "\"google:verbatimrelevance\":9998,"
1429 "\"google:suggestrelevance\":[9999]}]",
1430 { { "a.com", true }, { "a", true }, kEmptyExpectedMatch,
1431 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1432 ".com" },
1433 { "[\"a\",[\"http://a.com\"],[],[],"
1434 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1435 "\"google:verbatimrelevance\":0,"
1436 "\"google:suggestrelevance\":[9999]}]",
1437 { { "a.com", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1438 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1439 ".com" },
1440 { "[\"a\",[\"http://a.com\"],[],[],"
1441 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1442 "\"google:verbatimrelevance\":-1,"
1443 "\"google:suggestrelevance\":[9999]}]",
1444 { { "a.com", true }, { "a", true }, kEmptyExpectedMatch,
1445 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1446 ".com" },
1448 // Ensure that both types of relevance scores reorder matches together.
1449 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1450 "\"google:verbatimrelevance\":9998}]",
1451 { { "a1", true }, { "a", true }, { "a2", false }, kEmptyExpectedMatch,
1452 kEmptyExpectedMatch, kEmptyExpectedMatch },
1453 "1" },
1455 // Check that an inlineable result appears first regardless of its score.
1456 // Also, if the result set lacks a single inlineable result, abandon the
1457 // request to suppress verbatim (verbatim_relevance=0), which will then
1458 // cause verbatim to appear (first).
1459 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1460 { { "a", true }, { "b", false }, kEmptyExpectedMatch,
1461 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1462 std::string() },
1463 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1464 "\"google:verbatimrelevance\":0}]",
1465 { { "a", true }, { "b", false }, kEmptyExpectedMatch,
1466 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1467 std::string() },
1468 { "[\"a\",[\"http://b.com\"],[],[],"
1469 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1470 "\"google:suggestrelevance\":[9999]}]",
1471 { { "a", true }, { "b.com", false }, kEmptyExpectedMatch,
1472 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1473 std::string() },
1474 { "[\"a\",[\"http://b.com\"],[],[],"
1475 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1476 "\"google:suggestrelevance\":[9999],"
1477 "\"google:verbatimrelevance\":0}]",
1478 { { "a", true }, { "b.com", false }, kEmptyExpectedMatch,
1479 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1480 std::string() },
1482 // Allow low-scoring matches.
1483 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1484 { { "a1", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1485 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1486 "1" },
1487 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":10}]",
1488 { { "a1", true }, { "a", true }, kEmptyExpectedMatch,
1489 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1490 "1" },
1491 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[10],"
1492 "\"google:verbatimrelevance\":0}]",
1493 { { "a1", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1494 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1495 "1" },
1496 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 20],"
1497 "\"google:verbatimrelevance\":0}]",
1498 { { "a2", true }, { "a1", false }, kEmptyExpectedMatch,
1499 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1500 "2" },
1501 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 30],"
1502 "\"google:verbatimrelevance\":20}]",
1503 { { "a2", true }, { "a", true }, { "a1", false }, kEmptyExpectedMatch,
1504 kEmptyExpectedMatch, kEmptyExpectedMatch },
1505 "2" },
1506 { "[\"a\",[\"http://a.com\"],[],[],"
1507 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1508 "\"google:suggestrelevance\":[10],"
1509 "\"google:verbatimrelevance\":0}]",
1510 { { "a.com", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1511 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1512 ".com" },
1513 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1514 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1515 "\"google:suggestrelevance\":[10, 20],"
1516 "\"google:verbatimrelevance\":0}]",
1517 { { "a2.com", true }, { "a1.com", false }, kEmptyExpectedMatch,
1518 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1519 "2.com" },
1521 // Ensure that all suggestions are considered, regardless of order.
1522 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1523 "{\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
1524 { { "a", true }, { "h", false }, { "g", false }, { "f", false },
1525 { "e", false }, { "d", false } },
1526 std::string() },
1527 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1528 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1529 "\"http://h.com\"],[],[],"
1530 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1531 "\"NAVIGATION\", \"NAVIGATION\","
1532 "\"NAVIGATION\", \"NAVIGATION\","
1533 "\"NAVIGATION\"],"
1534 "\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
1535 { { "a", true }, { "h.com", false }, { "g.com", false },
1536 { "f.com", false }, { "e.com", false }, { "d.com", false } },
1537 std::string() },
1539 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1540 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10]}]",
1541 { { "a", true }, { "a1", false }, { "a2", false }, kEmptyExpectedMatch,
1542 kEmptyExpectedMatch, kEmptyExpectedMatch },
1543 std::string() },
1544 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 10]}]",
1545 { { "a", true }, { "a1", false }, kEmptyExpectedMatch,
1546 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1547 std::string() },
1548 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1549 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1550 "\"google:suggestrelevance\":[10]}]",
1551 { { "a", true }, { "a1.com", false }, kEmptyExpectedMatch,
1552 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1553 std::string() },
1554 { "[\"a\",[\"http://a1.com\"],[],[],"
1555 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1556 "\"google:suggestrelevance\":[9999, 10]}]",
1557 { { "a", true }, { "a1.com", false }, kEmptyExpectedMatch,
1558 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1559 std::string() },
1561 // Ensure that all 'verbatim' results are merged with their maximum score.
1562 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1563 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1564 { { "a2", true }, { "a", true }, { "a1", false }, kEmptyExpectedMatch,
1565 kEmptyExpectedMatch, kEmptyExpectedMatch },
1566 "2" },
1567 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1568 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1569 "\"google:verbatimrelevance\":0}]",
1570 { { "a2", true }, { "a", true }, { "a1", false }, kEmptyExpectedMatch,
1571 kEmptyExpectedMatch, kEmptyExpectedMatch },
1572 "2" },
1574 // Ensure that verbatim is always generated without other suggestions.
1575 // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1576 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1577 { { "a", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1578 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1579 std::string() },
1580 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1581 { { "a", true }, kEmptyExpectedMatch, kEmptyExpectedMatch,
1582 kEmptyExpectedMatch, kEmptyExpectedMatch, kEmptyExpectedMatch },
1583 std::string() },
1586 for (size_t i = 0; i < arraysize(cases); ++i) {
1587 // Send the query twice in order to have a synchronous pass after the first
1588 // response is received. This is necessary because SearchProvider doesn't
1589 // allow an asynchronous response to change the default match.
1590 for (size_t j = 0; j < 2; ++j) {
1591 QueryForInputAndWaitForFetcherResponses(
1592 ASCIIToUTF16("a"), false, cases[i].json, std::string());
1595 const std::string description = "for input with json=" + cases[i].json;
1596 CheckMatches(description, arraysize(cases[i].matches), cases[i].matches,
1597 provider_->matches());
1601 // Verifies that suggest results with relevance scores are added
1602 // properly when using the keyword fetcher. This is similar to the
1603 // test DefaultFetcherSuggestRelevance above but this uses inputs that
1604 // trigger keyword suggestions (i.e., "k a" rather than "a") and has
1605 // different expectations (because now the results are a mix of
1606 // keyword suggestions and default provider suggestions). When a new
1607 // test is added to this TEST_F, please consider if it would be
1608 // appropriate to add to DefaultFetcherSuggestRelevance as well.
1609 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1610 struct KeywordFetcherMatch {
1611 std::string contents;
1612 bool from_keyword;
1613 bool allowed_to_be_default_match;
1615 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1616 struct {
1617 const std::string json;
1618 const KeywordFetcherMatch matches[6];
1619 const std::string inline_autocompletion;
1620 } cases[] = {
1621 // Ensure that suggest relevance scores reorder matches and that
1622 // the keyword verbatim (lacking a suggested verbatim score) beats
1623 // the default provider verbatim.
1624 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1625 { { "a", true, true },
1626 { "k a", false, false },
1627 { "c", true, false },
1628 { "b", true, false },
1629 kEmptyMatch, kEmptyMatch },
1630 std::string() },
1631 // Again, check that relevance scores reorder matches, just this
1632 // time with navigation matches. This also checks that with
1633 // suggested relevance scores we allow multiple navsuggest results.
1634 // Note that navsuggest results that come from a keyword provider
1635 // are marked as not a keyword result. (They don't go to a
1636 // keyword search engine.)
1637 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1638 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1639 "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1640 { { "a", true, true },
1641 { "d", true, false },
1642 { "c.com", false, false },
1643 { "b.com", false, false },
1644 { "k a", false, false },
1645 kEmptyMatch },
1646 std::string() },
1648 // Without suggested relevance scores, we should only allow one
1649 // navsuggest result to be be displayed.
1650 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1651 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1652 { { "a", true, true },
1653 { "b.com", false, false },
1654 { "k a", false, false },
1655 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1656 std::string() },
1658 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1659 // Negative values will have no effect; the calculated value will be used.
1660 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1661 "\"google:suggestrelevance\":[9998]}]",
1662 { { "a", true, true },
1663 { "a1", true, false },
1664 { "k a", false, false },
1665 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1666 std::string() },
1667 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1668 "\"google:suggestrelevance\":[9999]}]",
1669 { { "a1", true, true },
1670 { "a", true, true },
1671 { "k a", false, false },
1672 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1673 "1" },
1674 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1675 "\"google:suggestrelevance\":[9999]}]",
1676 { { "a1", true, true },
1677 { "k a", false, false },
1678 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1679 "1" },
1680 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1681 "\"google:suggestrelevance\":[9999]}]",
1682 { { "a1", true, true },
1683 { "a", true, true },
1684 { "k a", false, false },
1685 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1686 "1" },
1687 { "[\"a\",[\"http://a.com\"],[],[],"
1688 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1689 "\"google:verbatimrelevance\":9999,"
1690 "\"google:suggestrelevance\":[9998]}]",
1691 { { "a", true, true },
1692 { "a.com", false, false },
1693 { "k a", false, false },
1694 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1695 std::string() },
1697 // Ensure that both types of relevance scores reorder matches together.
1698 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1699 "\"google:verbatimrelevance\":9998}]",
1700 { { "a1", true, true },
1701 { "a", true, true },
1702 { "a2", true, false },
1703 { "k a", false, false },
1704 kEmptyMatch, kEmptyMatch },
1705 "1" },
1707 // Check that an inlineable match appears first regardless of its score.
1708 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1709 { { "a", true, true },
1710 { "b", true, false },
1711 { "k a", false, false },
1712 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1713 std::string() },
1714 { "[\"a\",[\"http://b.com\"],[],[],"
1715 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1716 "\"google:suggestrelevance\":[9999]}]",
1717 { { "a", true, true },
1718 { "b.com", false, false },
1719 { "k a", false, false },
1720 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1721 std::string() },
1722 // If there is no inlineable match, restore the keyword verbatim score.
1723 // The keyword verbatim match will then appear first.
1724 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1725 "\"google:verbatimrelevance\":0}]",
1726 { { "a", true, true },
1727 { "b", true, false },
1728 { "k a", false, false },
1729 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1730 std::string() },
1731 { "[\"a\",[\"http://b.com\"],[],[],"
1732 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1733 "\"google:suggestrelevance\":[9999],"
1734 "\"google:verbatimrelevance\":0}]",
1735 { { "a", true, true },
1736 { "b.com", false, false },
1737 { "k a", false, false },
1738 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1739 std::string() },
1741 // The top result does not have to score as highly as calculated
1742 // verbatim. i.e., there are no minimum score restrictions in
1743 // this provider.
1744 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1745 { { "a1", true, true },
1746 { "k a", false, false },
1747 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1748 "1" },
1749 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":10}]",
1750 { { "a1", true, true },
1751 { "k a", false, false },
1752 { "a", true, true },
1753 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1754 "1" },
1755 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[10],"
1756 "\"google:verbatimrelevance\":0}]",
1757 { { "a1", true, true },
1758 { "k a", false, false },
1759 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1760 "1" },
1761 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 20],"
1762 "\"google:verbatimrelevance\":0}]",
1763 { { "a2", true, true },
1764 { "k a", false, false },
1765 { "a1", true, false },
1766 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1767 "2" },
1768 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[10, 30],"
1769 "\"google:verbatimrelevance\":20}]",
1770 { { "a2", true, true },
1771 { "k a", false, false },
1772 { "a", true, true },
1773 { "a1", true, false },
1774 kEmptyMatch, kEmptyMatch },
1775 "2" },
1777 // Ensure that all suggestions are considered, regardless of order.
1778 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1779 "{\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
1780 { { "a", true, true },
1781 { "k a", false, false },
1782 { "h", true, false },
1783 { "g", true, false },
1784 { "f", true, false },
1785 { "e", true, false } },
1786 std::string() },
1787 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1788 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1789 "\"http://h.com\"],[],[],"
1790 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1791 "\"NAVIGATION\", \"NAVIGATION\","
1792 "\"NAVIGATION\", \"NAVIGATION\","
1793 "\"NAVIGATION\"],"
1794 "\"google:suggestrelevance\":[10, 20, 30, 40, 50, 60, 70]}]",
1795 { { "a", true, true },
1796 { "k a", false, false },
1797 { "h.com", false, false },
1798 { "g.com", false, false },
1799 { "f.com", false, false },
1800 { "e.com", false, false } },
1801 std::string() },
1803 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1804 // Note that keyword suggestions by default (not in suggested relevance
1805 // mode) score more highly than the default verbatim.
1806 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1807 { { "a", true, true },
1808 { "a1", true, false },
1809 { "a2", true, false },
1810 { "k a", false, false },
1811 kEmptyMatch, kEmptyMatch },
1812 std::string() },
1813 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1814 { { "a", true, true },
1815 { "a1", true, false },
1816 { "k a", false, false },
1817 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1818 std::string() },
1819 // In this case, ignoring the suggested relevance scores means we keep
1820 // only one navsuggest result.
1821 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1822 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1823 "\"google:suggestrelevance\":[1]}]",
1824 { { "a", true, true },
1825 { "a1.com", false, false },
1826 { "k a", false, false },
1827 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1828 std::string() },
1829 { "[\"a\",[\"http://a1.com\"],[],[],"
1830 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1831 "\"google:suggestrelevance\":[9999, 1]}]",
1832 { { "a", true, true },
1833 { "a1.com", false, false },
1834 { "k a", false, false },
1835 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1836 std::string() },
1838 // Ensure that all 'verbatim' results are merged with their maximum score.
1839 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1840 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1841 { { "a2", true, true },
1842 { "a", true, true },
1843 { "a1", true, false },
1844 { "k a", false, false },
1845 kEmptyMatch, kEmptyMatch },
1846 "2" },
1847 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1848 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1849 "\"google:verbatimrelevance\":0}]",
1850 { { "a2", true, true },
1851 { "a", true, true },
1852 { "a1", true, false },
1853 { "k a", false, false },
1854 kEmptyMatch, kEmptyMatch },
1855 "2" },
1857 // Ensure that verbatim is always generated without other suggestions.
1858 // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1859 // (except when suggested relevances are ignored).
1860 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1861 { { "a", true, true },
1862 { "k a", false, false },
1863 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1864 std::string() },
1865 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1866 { { "a", true, true },
1867 { "k a", false, false },
1868 kEmptyMatch, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1869 std::string() },
1871 // In reorder mode, navsuggestions will not need to be demoted (because
1872 // they are marked as not allowed to be default match and will be
1873 // reordered as necessary).
1874 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1875 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1876 "\"google:verbatimrelevance\":9990,"
1877 "\"google:suggestrelevance\":[9998, 9999]}]",
1878 { { "a", true, true },
1879 { "a2.com", false, false },
1880 { "a1.com", false, false },
1881 { "k a", false, false },
1882 kEmptyMatch, kEmptyMatch },
1883 std::string() },
1884 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1885 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1886 "\"google:verbatimrelevance\":9990,"
1887 "\"google:suggestrelevance\":[9999, 9998]}]",
1888 { { "a", true, true },
1889 { "a1.com", false, false },
1890 { "a2.com", false, false },
1891 { "k a", false, false },
1892 kEmptyMatch, kEmptyMatch },
1893 std::string() },
1894 { "[\"a\",[\"https://a/\"],[],[],"
1895 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1896 "\"google:suggestrelevance\":[9999]}]",
1897 { { "a", true, true },
1898 { "https://a", false, false },
1899 { "k a", false, false },
1900 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1901 std::string() },
1902 // Check when navsuggest scores more than verbatim and there is query
1903 // suggestion but it scores lower.
1904 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1905 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1906 "\"google:verbatimrelevance\":9990,"
1907 "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
1908 { { "a", true, true },
1909 { "a2.com", false, false },
1910 { "a1.com", false, false },
1911 { "a3", true, false },
1912 { "k a", false, false },
1913 kEmptyMatch },
1914 std::string() },
1915 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1916 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1917 "\"google:verbatimrelevance\":9990,"
1918 "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
1919 { { "a", true, true },
1920 { "a1.com", false, false },
1921 { "a2.com", false, false },
1922 { "a3", true, false },
1923 { "k a", false, false },
1924 kEmptyMatch },
1925 std::string() },
1926 // Check when navsuggest scores more than a query suggestion. There is
1927 // a verbatim but it scores lower.
1928 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1929 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1930 "\"google:verbatimrelevance\":9990,"
1931 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1932 { { "a3", true, true },
1933 { "a2.com", false, false },
1934 { "a1.com", false, false },
1935 { "a", true, true },
1936 { "k a", false, false },
1937 kEmptyMatch },
1938 "3" },
1939 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1940 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1941 "\"google:verbatimrelevance\":9990,"
1942 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1943 { { "a3", true, true },
1944 { "a1.com", false, false },
1945 { "a2.com", false, false },
1946 { "a", true, true },
1947 { "k a", false, false },
1948 kEmptyMatch },
1949 "3" },
1950 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1951 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1952 "\"google:verbatimrelevance\":0,"
1953 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1954 { { "a3", true, true },
1955 { "a2.com", false, false },
1956 { "a1.com", false, false },
1957 { "k a", false, false },
1958 kEmptyMatch, kEmptyMatch },
1959 "3" },
1960 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1961 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1962 "\"google:verbatimrelevance\":0,"
1963 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1964 { { "a3", true, true },
1965 { "a1.com", false, false },
1966 { "a2.com", false, false },
1967 { "k a", false, false },
1968 kEmptyMatch, kEmptyMatch },
1969 "3" },
1970 // Check when there is neither verbatim nor a query suggestion that,
1971 // because we can't demote navsuggestions below a query suggestion,
1972 // we restore the keyword verbatim score.
1973 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1974 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1975 "\"google:verbatimrelevance\":0,"
1976 "\"google:suggestrelevance\":[9998, 9999]}]",
1977 { { "a", true, true },
1978 { "a2.com", false, false },
1979 { "a1.com", false, false },
1980 { "k a", false, false },
1981 kEmptyMatch, kEmptyMatch },
1982 std::string() },
1983 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1984 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1985 "\"google:verbatimrelevance\":0,"
1986 "\"google:suggestrelevance\":[9999, 9998]}]",
1987 { { "a", true, true },
1988 { "a1.com", false, false },
1989 { "a2.com", false, false },
1990 { "k a", false, false },
1991 kEmptyMatch, kEmptyMatch },
1992 std::string() },
1993 // More checks that everything works when it's not necessary to demote.
1994 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1995 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1996 "\"google:verbatimrelevance\":9990,"
1997 "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
1998 { { "a3", true, true },
1999 { "a2.com", false, false },
2000 { "a1.com", false, false },
2001 { "a", true, true },
2002 { "k a", false, false },
2003 kEmptyMatch },
2004 "3" },
2005 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2006 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2007 "\"google:verbatimrelevance\":9990,"
2008 "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
2009 { { "a3", true, true },
2010 { "a1.com", false, false },
2011 { "a2.com", false, false },
2012 { "a", true, true },
2013 { "k a", false, false },
2014 kEmptyMatch },
2015 "3" },
2018 for (size_t i = 0; i < arraysize(cases); ++i) {
2019 // Send the query twice in order to have a synchronous pass after the first
2020 // response is received. This is necessary because SearchProvider doesn't
2021 // allow an asynchronous response to change the default match.
2022 for (size_t j = 0; j < 2; ++j) {
2023 QueryForInput(ASCIIToUTF16("k a"), false, true);
2025 // Set up a default fetcher with no results.
2026 net::TestURLFetcher* default_fetcher =
2027 test_factory_.GetFetcherByID(
2028 SearchProvider::kDefaultProviderURLFetcherID);
2029 ASSERT_TRUE(default_fetcher);
2030 default_fetcher->set_response_code(200);
2031 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
2032 default_fetcher = NULL;
2034 // Set up a keyword fetcher with provided results.
2035 net::TestURLFetcher* keyword_fetcher =
2036 test_factory_.GetFetcherByID(
2037 SearchProvider::kKeywordProviderURLFetcherID);
2038 ASSERT_TRUE(keyword_fetcher);
2039 keyword_fetcher->set_response_code(200);
2040 keyword_fetcher->SetResponseString(cases[i].json);
2041 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2042 keyword_fetcher = NULL;
2043 RunTillProviderDone();
2046 SCOPED_TRACE("for input with json=" + cases[i].json);
2047 const ACMatches& matches = provider_->matches();
2048 ASSERT_FALSE(matches.empty());
2049 // Find the first match that's allowed to be the default match and check
2050 // its inline_autocompletion.
2051 ACMatches::const_iterator it = FindDefaultMatch(matches);
2052 ASSERT_NE(matches.end(), it);
2053 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2054 it->inline_autocompletion);
2056 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
2057 size_t j = 0;
2058 // Ensure that the returned matches equal the expectations.
2059 for (; j < matches.size(); ++j) {
2060 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
2061 matches[j].contents);
2062 EXPECT_EQ(cases[i].matches[j].from_keyword,
2063 matches[j].keyword == ASCIIToUTF16("k"));
2064 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
2065 matches[j].allowed_to_be_default_match);
2067 // Ensure that no expected matches are missing.
2068 for (; j < arraysize(cases[i].matches); ++j) {
2069 SCOPED_TRACE(" Case # " + base::SizeTToString(i));
2070 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents);
2075 TEST_F(SearchProviderTest, DontInlineAutocompleteAsynchronously) {
2076 // This test sends two separate queries, each receiving different JSON
2077 // replies, and checks that at each stage of processing (receiving first
2078 // asynchronous response, handling new keystroke synchronously / sending the
2079 // second request, and receiving the second asynchronous response) we have the
2080 // expected matches. In particular, receiving the second response shouldn't
2081 // cause an unexpected inline autcompletion.
2082 struct {
2083 const std::string first_json;
2084 const ExpectedMatch first_async_matches[4];
2085 const ExpectedMatch sync_matches[4];
2086 const std::string second_json;
2087 const ExpectedMatch second_async_matches[4];
2088 } cases[] = {
2089 // A simple test that verifies we don't inline autocomplete after the
2090 // first asynchronous response, but we do at the next keystroke if the
2091 // response's results were good enough. Furthermore, we should continue
2092 // inline autocompleting after the second asynchronous response if the new
2093 // top suggestion is the same as the old inline autocompleted suggestion.
2094 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2095 "{\"google:verbatimrelevance\":9000,"
2096 "\"google:suggestrelevance\":[9002, 9001]}]",
2097 { { "a", true }, { "ab1", false }, { "ab2", false },
2098 kEmptyExpectedMatch },
2099 { { "ab1", true }, { "ab2", true }, { "ab", true },
2100 kEmptyExpectedMatch },
2101 "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
2102 "{\"google:verbatimrelevance\":9000,"
2103 "\"google:suggestrelevance\":[9002, 9001]}]",
2104 { { "ab1", true }, { "ab2", false }, { "ab", true },
2105 kEmptyExpectedMatch } },
2106 // Ditto, just for a navigation suggestion.
2107 { "[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
2108 "{\"google:verbatimrelevance\":9000,"
2109 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2110 "\"google:suggestrelevance\":[9002, 9001]}]",
2111 { { "a", true }, { "ab1.com", false }, { "ab2.com", false },
2112 kEmptyExpectedMatch },
2113 { { "ab1.com", true }, { "ab2.com", true }, { "ab", true },
2114 kEmptyExpectedMatch },
2115 "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
2116 "{\"google:verbatimrelevance\":9000,"
2117 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2118 "\"google:suggestrelevance\":[9002, 9001]}]",
2119 { { "ab1.com", true }, { "ab2.com", false }, { "ab", true },
2120 kEmptyExpectedMatch } },
2121 // A more realistic test of the same situation.
2122 { "[\"a\",[\"abcdef\", \"abcdef.com\", \"abc\"],[],[],"
2123 "{\"google:verbatimrelevance\":900,"
2124 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"QUERY\"],"
2125 "\"google:suggestrelevance\":[1250, 1200, 1000]}]",
2126 { { "a", true }, { "abcdef", false }, { "abcdef.com", false },
2127 { "abc", false } },
2128 { { "abcdef", true }, { "abcdef.com", true }, { "abc", true },
2129 { "ab", true } },
2130 "[\"ab\",[\"abcdef\", \"abcdef.com\", \"abc\"],[],[],"
2131 "{\"google:verbatimrelevance\":900,"
2132 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"QUERY\"],"
2133 "\"google:suggestrelevance\":[1250, 1200, 1000]}]",
2134 { { "abcdef", true }, { "abcdef.com", false }, { "abc", false },
2135 { "ab", true } } },
2137 // Without an original inline autcompletion, a new inline autcompletion
2138 // should be rejected.
2139 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2140 "{\"google:verbatimrelevance\":9000,"
2141 "\"google:suggestrelevance\":[8000, 7000]}]",
2142 { { "a", true }, { "ab1", false }, { "ab2", false },
2143 kEmptyExpectedMatch },
2144 { { "ab", true }, { "ab1", true }, { "ab2", true },
2145 kEmptyExpectedMatch },
2146 "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
2147 "{\"google:verbatimrelevance\":9000,"
2148 "\"google:suggestrelevance\":[9002, 9001]}]",
2149 { { "ab", true }, { "ab1", false }, { "ab2", false },
2150 kEmptyExpectedMatch } },
2151 // For the same test except with the queries scored in the opposite order
2152 // on the second JSON response, the queries should be ordered by the second
2153 // response's scores, not the first.
2154 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2155 "{\"google:verbatimrelevance\":9000,"
2156 "\"google:suggestrelevance\":[8000, 7000]}]",
2157 { { "a", true }, { "ab1", false }, { "ab2", false },
2158 kEmptyExpectedMatch },
2159 { { "ab", true }, { "ab1", true }, { "ab2", true },
2160 kEmptyExpectedMatch },
2161 "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
2162 "{\"google:verbatimrelevance\":9000,"
2163 "\"google:suggestrelevance\":[9001, 9002]}]",
2164 { { "ab", true }, { "ab2", false }, { "ab1", false },
2165 kEmptyExpectedMatch } },
2166 // Now, the same verifications but with the new inline autocompletion as a
2167 // navsuggestion. The new autocompletion should still be rejected.
2168 { "[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
2169 "{\"google:verbatimrelevance\":9000,"
2170 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2171 "\"google:suggestrelevance\":[8000, 7000]}]",
2172 { { "a", true }, { "ab1.com", false }, { "ab2.com", false },
2173 kEmptyExpectedMatch },
2174 { { "ab", true }, { "ab1.com", true }, { "ab2.com", true },
2175 kEmptyExpectedMatch },
2176 "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
2177 "{\"google:verbatimrelevance\":9000,"
2178 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2179 "\"google:suggestrelevance\":[9002, 9001]}]",
2180 { { "ab", true }, { "ab1.com", false }, { "ab2.com", false },
2181 kEmptyExpectedMatch } },
2182 { "[\"a\",[\"ab1.com\", \"ab2.com\"],[],[],"
2183 "{\"google:verbatimrelevance\":9000,"
2184 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2185 "\"google:suggestrelevance\":[8000, 7000]}]",
2186 { { "a", true }, { "ab1.com", false }, { "ab2.com", false },
2187 kEmptyExpectedMatch },
2188 { { "ab", true }, { "ab1.com", true }, { "ab2.com", true },
2189 kEmptyExpectedMatch },
2190 "[\"ab\",[\"ab1.com\", \"ab2.com\"],[],[],"
2191 "{\"google:verbatimrelevance\":9000,"
2192 "\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2193 "\"google:suggestrelevance\":[9001, 9002]}]",
2194 { { "ab", true }, { "ab2.com", false }, { "ab1.com", false },
2195 kEmptyExpectedMatch } },
2197 // It's okay to abandon an inline autocompletion asynchronously.
2198 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2199 "{\"google:verbatimrelevance\":9000,"
2200 "\"google:suggestrelevance\":[9002, 9001]}]",
2201 { { "a", true }, { "ab1", false }, { "ab2", false },
2202 kEmptyExpectedMatch },
2203 { { "ab1", true }, { "ab2", true }, { "ab", true },
2204 kEmptyExpectedMatch },
2205 "[\"ab\",[\"ab1\", \"ab2\"],[],[],"
2206 "{\"google:verbatimrelevance\":9000,"
2207 "\"google:suggestrelevance\":[8000, 7000]}]",
2208 { { "ab", true }, { "ab1", true }, { "ab2", false },
2209 kEmptyExpectedMatch } },
2211 // Note: it's possible that the suggest server returns a suggestion with
2212 // an inline autocompletion (that as usual we delay in allowing it to
2213 // be displayed as an inline autocompletion until the next keystroke),
2214 // then, in response to the next keystroke, the server returns a different
2215 // suggestion as an inline autocompletion. This is not likely to happen.
2216 // Regardless, if it does, one could imagine three different behaviors:
2217 // - keep the original inline autocompletion until the next keystroke
2218 // (i.e., don't abandon an inline autocompletion asynchronously), then
2219 // use the new suggestion
2220 // - abandon all inline autocompletions upon the server response, then use
2221 // the new suggestion on the next keystroke
2222 // - ignore the new inline autocompletion provided by the server, yet
2223 // possibly keep the original if it scores well in the most recent
2224 // response, then use the new suggestion on the next keystroke
2225 // All of these behaviors are reasonable. The main thing we want to
2226 // ensure is that the second asynchronous response shouldn't cause *a new*
2227 // inline autocompletion to be displayed. We test that here.
2228 // The current implementation does the third bullet, but all of these
2229 // behaviors seem reasonable.
2230 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2231 "{\"google:verbatimrelevance\":9000,"
2232 "\"google:suggestrelevance\":[9002, 9001]}]",
2233 { { "a", true }, { "ab1", false }, { "ab2", false },
2234 kEmptyExpectedMatch },
2235 { { "ab1", true }, { "ab2", true }, { "ab", true },
2236 kEmptyExpectedMatch },
2237 "[\"ab\",[\"ab1\", \"ab3\"],[],[],"
2238 "{\"google:verbatimrelevance\":9000,"
2239 "\"google:suggestrelevance\":[9002, 9900]}]",
2240 { { "ab1", true }, { "ab3", false }, { "ab", true },
2241 kEmptyExpectedMatch } },
2242 { "[\"a\",[\"ab1\", \"ab2\"],[],[],"
2243 "{\"google:verbatimrelevance\":9000,"
2244 "\"google:suggestrelevance\":[9002, 9001]}]",
2245 { { "a", true }, { "ab1", false }, { "ab2", false },
2246 kEmptyExpectedMatch },
2247 { { "ab1", true }, { "ab2", true }, { "ab", true },
2248 kEmptyExpectedMatch },
2249 "[\"ab\",[\"ab1\", \"ab3\"],[],[],"
2250 "{\"google:verbatimrelevance\":9000,"
2251 "\"google:suggestrelevance\":[8000, 9500]}]",
2252 { { "ab", true }, { "ab3", false }, { "ab1", true },
2253 kEmptyExpectedMatch } },
2256 for (size_t i = 0; i < arraysize(cases); ++i) {
2257 // First, send the query "a" and receive the JSON response |first_json|.
2258 ClearAllResults();
2259 QueryForInputAndWaitForFetcherResponses(
2260 ASCIIToUTF16("a"), false, cases[i].first_json, std::string());
2262 // Verify that the matches after the asynchronous results are as expected.
2263 std::string description = "first asynchronous response for input with "
2264 "first_json=" + cases[i].first_json;
2265 CheckMatches(description, arraysize(cases[i].first_async_matches),
2266 cases[i].first_async_matches, provider_->matches());
2268 // Then, send the query "ab" and check the synchronous matches.
2269 description = "synchronous response after the first keystroke after input "
2270 "with first_json=" + cases[i].first_json;
2271 QueryForInput(ASCIIToUTF16("ab"), false, false);
2272 CheckMatches(description, arraysize(cases[i].sync_matches),
2273 cases[i].sync_matches, provider_->matches());
2275 // Finally, get the provided JSON response, |second_json|, and verify the
2276 // matches after the second asynchronous response are as expected.
2277 description = "second asynchronous response after input with first_json=" +
2278 cases[i].first_json + " and second_json=" + cases[i].second_json;
2279 net::TestURLFetcher* second_fetcher =
2280 test_factory_.GetFetcherByID(
2281 SearchProvider::kDefaultProviderURLFetcherID);
2282 ASSERT_TRUE(second_fetcher);
2283 second_fetcher->set_response_code(200);
2284 second_fetcher->SetResponseString(cases[i].second_json);
2285 second_fetcher->delegate()->OnURLFetchComplete(second_fetcher);
2286 RunTillProviderDone();
2287 CheckMatches(description, arraysize(cases[i].second_async_matches),
2288 cases[i].second_async_matches, provider_->matches());
2292 TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
2293 // We hardcode the string "term1" below, so ensure that the search term that
2294 // got added to history already is that string.
2295 ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
2296 base::string16 term = term1_.substr(0, term1_.length() - 1);
2298 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
2299 profile_.BlockUntilHistoryProcessesPendingRequests();
2301 struct {
2302 const base::string16 input;
2303 const std::string json;
2304 const std::string matches[6];
2305 } cases[] = {
2306 // The history results outscore the default verbatim score. term2 has more
2307 // visits so it outscores term1. The suggestions are still returned since
2308 // they're server-scored.
2309 { term,
2310 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2311 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2312 "\"google:suggestrelevance\":[1, 2, 3]}]",
2313 { "term2", "term1", "term", "a3", "a2", "a1" } },
2314 // Because we already have three suggestions by the time we see the history
2315 // results, they don't get returned.
2316 { term,
2317 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2318 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2319 "\"google:verbatimrelevance\":1450,"
2320 "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
2321 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
2322 // If we only have two suggestions, we have room for a history result.
2323 { term,
2324 "[\"term\",[\"a1\", \"a2\"],[],[],"
2325 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2326 "\"google:verbatimrelevance\":1450,"
2327 "\"google:suggestrelevance\":[1430, 1410]}]",
2328 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
2329 // If we have more than three suggestions, they should all be returned as
2330 // long as we have enough total space for them.
2331 { term,
2332 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2333 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2334 "\"google:verbatimrelevance\":1450,"
2335 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
2336 { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
2337 { term,
2338 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
2339 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
2340 "\"QUERY\", \"QUERY\"],"
2341 "\"google:verbatimrelevance\":1450,"
2342 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
2343 { "term", "a1", "a2", "a3", "a4", "a5" } },
2344 { term,
2345 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2346 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2347 "\"google:verbatimrelevance\":1450,"
2348 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
2349 { "term", "a1", "a2", "term2", "a3", "a4" } }
2352 for (size_t i = 0; i < arraysize(cases); ++i) {
2353 QueryForInputAndWaitForFetcherResponses(
2354 cases[i].input, false, cases[i].json, std::string());
2356 const std::string description = "for input with json=" + cases[i].json;
2357 const ACMatches& matches = provider_->matches();
2359 // Ensure no extra matches are present.
2360 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
2362 size_t j = 0;
2363 // Ensure that the returned matches equal the expectations.
2364 for (; j < matches.size(); ++j)
2365 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
2366 matches[j].contents) << description;
2367 // Ensure that no expected matches are missing.
2368 for (; j < arraysize(cases[i].matches); ++j)
2369 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
2370 "Case # " << i << " " << description;
2374 // Verifies suggest relevance behavior for URL input.
2375 TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
2376 struct DefaultFetcherUrlInputMatch {
2377 const std::string match_contents;
2378 AutocompleteMatch::Type match_type;
2379 bool allowed_to_be_default_match;
2381 const DefaultFetcherUrlInputMatch kEmptyMatch =
2382 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2383 struct {
2384 const std::string input;
2385 const std::string json;
2386 const DefaultFetcherUrlInputMatch output[4];
2387 } cases[] = {
2388 // Ensure NAVIGATION matches are allowed to be listed first for URL input.
2389 // Non-inlineable matches should not be allowed to be the default match.
2390 // Note that the top-scoring inlineable match is moved to the top
2391 // regardless of its score.
2392 { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[],"
2393 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2394 "\"google:suggestrelevance\":[9999]}]",
2395 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2396 { "b.com", AutocompleteMatchType::NAVSUGGEST, false },
2397 kEmptyMatch, kEmptyMatch } },
2398 { "a.com", "[\"a.com\",[\"https://b.com\"],[],[],"
2399 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2400 "\"google:suggestrelevance\":[9999]}]",
2401 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2402 { "https://b.com", AutocompleteMatchType::NAVSUGGEST, false },
2403 kEmptyMatch, kEmptyMatch } },
2404 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2405 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2406 "\"google:suggestrelevance\":[9999]}]",
2407 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true },
2408 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2409 kEmptyMatch, kEmptyMatch } },
2410 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2411 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2412 "\"google:suggestrelevance\":[9999]}]",
2413 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true },
2414 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2415 kEmptyMatch, kEmptyMatch } },
2417 // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL
2418 // input. SearchProvider disregards search and verbatim suggested
2419 // relevances.
2420 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2421 "{\"google:suggestrelevance\":[9999]}]",
2422 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2423 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2424 kEmptyMatch, kEmptyMatch } },
2425 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2426 "{\"google:suggestrelevance\":[9999]}]",
2427 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2428 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2429 kEmptyMatch, kEmptyMatch } },
2431 // Ensure the fallback mechanism allows inlineable NAVIGATION matches.
2432 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2433 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2434 "\"google:suggestrelevance\":[9999, 9998]}]",
2435 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2436 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2437 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2438 kEmptyMatch } },
2439 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2440 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2441 "\"google:suggestrelevance\":[9998, 9997],"
2442 "\"google:verbatimrelevance\":9999}]",
2443 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2444 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2445 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2446 kEmptyMatch } },
2448 // Ensure non-inlineable SUGGEST matches are allowed for URL input
2449 // assuming the best inlineable match is not a query (i.e., is a
2450 // NAVSUGGEST). The best inlineable match will be at the top of the
2451 // list regardless of its score.
2452 { "a.com", "[\"a.com\",[\"info\"],[],[],"
2453 "{\"google:suggestrelevance\":[9999]}]",
2454 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2455 { "info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2456 kEmptyMatch, kEmptyMatch } },
2457 { "a.com", "[\"a.com\",[\"info\"],[],[],"
2458 "{\"google:suggestrelevance\":[9999]}]",
2459 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2460 { "info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2461 kEmptyMatch, kEmptyMatch } },
2463 // Ensure that if the user explicitly enters a scheme, a navsuggest
2464 // result for a URL with a different scheme is not inlineable.
2465 { "http://a.com", "[\"http://a.com\","
2466 "[\"http://a.com/1\", \"https://a.com/\"],[],[],"
2467 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2468 "\"google:suggestrelevance\":[9000, 8000]}]",
2469 { { "http://a.com/1", AutocompleteMatchType::NAVSUGGEST, true },
2470 { "https://a.com", AutocompleteMatchType::NAVSUGGEST, false },
2471 { "http://a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2472 true },
2473 kEmptyMatch } },
2476 for (size_t i = 0; i < arraysize(cases); ++i) {
2477 // Send the query twice in order to have a synchronous pass after the first
2478 // response is received. This is necessary because SearchProvider doesn't
2479 // allow an asynchronous response to change the default match.
2480 for (size_t j = 0; j < 2; ++j) {
2481 QueryForInputAndWaitForFetcherResponses(
2482 ASCIIToUTF16(cases[i].input), false, cases[i].json, std::string());
2485 SCOPED_TRACE("input=" + cases[i].input + " json=" + cases[i].json);
2486 size_t j = 0;
2487 const ACMatches& matches = provider_->matches();
2488 ASSERT_LE(matches.size(), arraysize(cases[i].output));
2489 // Ensure that the returned matches equal the expectations.
2490 for (; j < matches.size(); ++j) {
2491 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2492 matches[j].contents);
2493 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2494 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2495 matches[j].allowed_to_be_default_match);
2497 // Ensure that no expected matches are missing.
2498 for (; j < arraysize(cases[i].output); ++j) {
2499 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2500 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2501 cases[i].output[j].match_type);
2502 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2507 // A basic test that verifies the field trial triggered parsing logic.
2508 TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2509 QueryForInputAndWaitForFetcherResponses(
2510 ASCIIToUTF16("foo"), false,
2511 "[\"foo\",[\"foo bar\"],[\"\"],[],"
2512 "{\"google:suggesttype\":[\"QUERY\"],"
2513 "\"google:fieldtrialtriggered\":true}]",
2514 std::string());
2517 // Check for the match and field trial triggered bits.
2518 AutocompleteMatch match;
2519 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
2520 ProvidersInfo providers_info;
2521 provider_->AddProviderInfo(&providers_info);
2522 ASSERT_EQ(1U, providers_info.size());
2523 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2524 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
2527 // Reset the session and check that bits are reset.
2528 provider_->ResetSession();
2529 ProvidersInfo providers_info;
2530 provider_->AddProviderInfo(&providers_info);
2531 ASSERT_EQ(1U, providers_info.size());
2532 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2533 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
2537 // Verifies inline autocompletion of navigational results.
2538 TEST_F(SearchProviderTest, NavigationInline) {
2539 struct {
2540 const std::string input;
2541 const std::string url;
2542 // Test the expected fill_into_edit, which may drop "http://".
2543 // Some cases do not trim "http://" to match from the start of the scheme.
2544 const std::string fill_into_edit;
2545 const std::string inline_autocompletion;
2546 const bool allowed_to_be_default_match_in_regular_mode;
2547 const bool allowed_to_be_default_match_in_prevent_inline_mode;
2548 } cases[] = {
2549 // Do not inline matches that do not contain the input; trim http as needed.
2550 { "x", "http://www.abc.com",
2551 "www.abc.com", std::string(), false, false },
2552 { "https:", "http://www.abc.com",
2553 "www.abc.com", std::string(), false, false },
2554 { "http://www.abc.com/a", "http://www.abc.com",
2555 "http://www.abc.com", std::string(), false,
2556 false },
2558 // Do not inline matches with invalid input prefixes; trim http as needed.
2559 { "ttp", "http://www.abc.com",
2560 "www.abc.com", std::string(), false, false },
2561 { "://w", "http://www.abc.com",
2562 "www.abc.com", std::string(), false, false },
2563 { "ww.", "http://www.abc.com",
2564 "www.abc.com", std::string(), false, false },
2565 { ".ab", "http://www.abc.com",
2566 "www.abc.com", std::string(), false, false },
2567 { "bc", "http://www.abc.com",
2568 "www.abc.com", std::string(), false, false },
2569 { ".com", "http://www.abc.com",
2570 "www.abc.com", std::string(), false, false },
2572 // Do not inline matches that omit input domain labels; trim http as needed.
2573 { "www.a", "http://a.com",
2574 "a.com", std::string(), false, false },
2575 { "http://www.a", "http://a.com",
2576 "http://a.com", std::string(), false, false },
2577 { "www.a", "ftp://a.com",
2578 "ftp://a.com", std::string(), false, false },
2579 { "ftp://www.a", "ftp://a.com",
2580 "ftp://a.com", std::string(), false, false },
2582 // Input matching but with nothing to inline will not yield an offset, but
2583 // will be allowed to be default.
2584 { "abc.com", "http://www.abc.com",
2585 "www.abc.com", std::string(), true, true },
2586 { "abc.com/", "http://www.abc.com",
2587 "www.abc.com", std::string(), true, true },
2588 { "http://www.abc.com", "http://www.abc.com",
2589 "http://www.abc.com", std::string(), true, true },
2590 { "http://www.abc.com/", "http://www.abc.com",
2591 "http://www.abc.com", std::string(), true, true },
2593 // Inputs with trailing whitespace should inline when possible.
2594 { "abc.com ", "http://www.abc.com",
2595 "www.abc.com", std::string(), true, true },
2596 { "abc.com/ ", "http://www.abc.com",
2597 "www.abc.com", std::string(), true, true },
2598 { "abc.com ", "http://www.abc.com/bar",
2599 "www.abc.com/bar", "/bar", false, false },
2601 // A suggestion that's equivalent to what the input gets fixed up to
2602 // should be inlined.
2603 { "abc.com:", "http://abc.com/",
2604 "abc.com", std::string(), true, true },
2605 { "abc.com:", "http://www.abc.com",
2606 "www.abc.com", std::string(), true, true },
2608 // Inline matches when the input is a leading substring of the scheme.
2609 { "h", "http://www.abc.com",
2610 "http://www.abc.com", "ttp://www.abc.com", true, false },
2611 { "http", "http://www.abc.com",
2612 "http://www.abc.com", "://www.abc.com", true, false },
2614 // Inline matches when the input is a leading substring of the full URL.
2615 { "http:", "http://www.abc.com",
2616 "http://www.abc.com", "//www.abc.com", true, false },
2617 { "http://w", "http://www.abc.com",
2618 "http://www.abc.com", "ww.abc.com", true, false },
2619 { "http://www.", "http://www.abc.com",
2620 "http://www.abc.com", "abc.com", true, false },
2621 { "http://www.ab", "http://www.abc.com",
2622 "http://www.abc.com", "c.com", true, false },
2623 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2624 "http://www.abc.com/path/file.htm?q=x#foo",
2625 "ath/file.htm?q=x#foo",
2626 true, false },
2627 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
2628 "http://abc.com/path/file.htm?q=x#foo",
2629 "ath/file.htm?q=x#foo",
2630 true, false},
2632 // Inline matches with valid URLPrefixes; only trim "http://".
2633 { "w", "http://www.abc.com",
2634 "www.abc.com", "ww.abc.com", true, false },
2635 { "www.a", "http://www.abc.com",
2636 "www.abc.com", "bc.com", true, false },
2637 { "abc", "http://www.abc.com",
2638 "www.abc.com", ".com", true, false },
2639 { "abc.c", "http://www.abc.com",
2640 "www.abc.com", "om", true, false },
2641 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2642 "www.abc.com/path/file.htm?q=x#foo",
2643 "ath/file.htm?q=x#foo",
2644 true, false },
2645 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
2646 "abc.com/path/file.htm?q=x#foo",
2647 "ath/file.htm?q=x#foo",
2648 true, false },
2650 // Inline matches using the maximal URLPrefix components.
2651 { "h", "http://help.com",
2652 "help.com", "elp.com", true, false },
2653 { "http", "http://http.com",
2654 "http.com", ".com", true, false },
2655 { "h", "http://www.help.com",
2656 "www.help.com", "elp.com", true, false },
2657 { "http", "http://www.http.com",
2658 "www.http.com", ".com", true, false },
2659 { "w", "http://www.www.com",
2660 "www.www.com", "ww.com", true, false },
2662 // Test similar behavior for the ftp and https schemes.
2663 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2664 "ftp://www.abc.com/path/file.htm?q=x#foo",
2665 "c.com/path/file.htm?q=x#foo", true, false },
2666 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2667 "ftp://www.abc.com/path/file.htm?q=x#foo",
2668 "c.com/path/file.htm?q=x#foo", true, false },
2669 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2670 "ftp://www.abc.com/path/file.htm?q=x#foo",
2671 "c.com/path/file.htm?q=x#foo", true, false },
2672 { "ab", "ftp://abc.com/path/file.htm?q=x#foo",
2673 "ftp://abc.com/path/file.htm?q=x#foo",
2674 "c.com/path/file.htm?q=x#foo", true, false },
2675 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
2676 "https://www.abc.com/path/file.htm?q=x#foo",
2677 "c.com/path/file.htm?q=x#foo",
2678 true, false },
2679 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
2680 "https://www.abc.com/path/file.htm?q=x#foo",
2681 "c.com/path/file.htm?q=x#foo", true, false },
2682 { "ab", "https://www.abc.com/path/file.htm?q=x#foo",
2683 "https://www.abc.com/path/file.htm?q=x#foo",
2684 "c.com/path/file.htm?q=x#foo", true, false },
2685 { "ab", "https://abc.com/path/file.htm?q=x#foo",
2686 "https://abc.com/path/file.htm?q=x#foo",
2687 "c.com/path/file.htm?q=x#foo", true, false },
2689 // Forced query input should inline and retain the "?" prefix.
2690 { "?http://www.ab", "http://www.abc.com",
2691 "?http://www.abc.com", "c.com", true, false },
2692 { "?www.ab", "http://www.abc.com",
2693 "?www.abc.com", "c.com", true, false },
2694 { "?ab", "http://www.abc.com",
2695 "?www.abc.com", "c.com", true, false },
2696 { "?abc.com", "http://www.abc.com",
2697 "?www.abc.com", std::string(), true, true },
2700 for (size_t i = 0; i < arraysize(cases); ++i) {
2701 // First test regular mode.
2702 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2703 SearchSuggestionParser::NavigationResult result(
2704 ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url),
2705 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2706 false, 0, false, ASCIIToUTF16(cases[i].input), std::string());
2707 result.set_received_after_last_keystroke(false);
2708 AutocompleteMatch match(provider_->NavigationToMatch(result));
2709 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2710 match.inline_autocompletion);
2711 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
2712 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_regular_mode,
2713 match.allowed_to_be_default_match);
2715 // Then test prevent-inline-autocomplete mode.
2716 QueryForInput(ASCIIToUTF16(cases[i].input), true, false);
2717 SearchSuggestionParser::NavigationResult result_prevent_inline(
2718 ChromeAutocompleteSchemeClassifier(&profile_), GURL(cases[i].url),
2719 AutocompleteMatchType::NAVSUGGEST, base::string16(), std::string(),
2720 false, 0, false, ASCIIToUTF16(cases[i].input), std::string());
2721 result_prevent_inline.set_received_after_last_keystroke(false);
2722 AutocompleteMatch match_prevent_inline(
2723 provider_->NavigationToMatch(result_prevent_inline));
2724 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2725 match_prevent_inline.inline_autocompletion);
2726 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit),
2727 match_prevent_inline.fill_into_edit);
2728 EXPECT_EQ(cases[i].allowed_to_be_default_match_in_prevent_inline_mode,
2729 match_prevent_inline.allowed_to_be_default_match);
2733 // Verifies that "http://" is not trimmed for input that is a leading substring.
2734 TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
2735 const base::string16 input(ASCIIToUTF16("ht"));
2736 const base::string16 url(ASCIIToUTF16("http://a.com"));
2737 SearchSuggestionParser::NavigationResult result(
2738 ChromeAutocompleteSchemeClassifier(&profile_), GURL(url),
2739 AutocompleteMatchType::NAVSUGGEST,
2740 base::string16(), std::string(), false, 0, false, input, std::string());
2741 result.set_received_after_last_keystroke(false);
2743 // Check the offset and strings when inline autocompletion is allowed.
2744 QueryForInput(input, false, false);
2745 AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
2746 EXPECT_EQ(url, match_inline.fill_into_edit);
2747 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
2748 EXPECT_TRUE(match_inline.allowed_to_be_default_match);
2749 EXPECT_EQ(url, match_inline.contents);
2751 // Check the same strings when inline autocompletion is prevented.
2752 QueryForInput(input, true, false);
2753 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
2754 EXPECT_EQ(url, match_prevent.fill_into_edit);
2755 EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
2756 EXPECT_EQ(url, match_prevent.contents);
2759 // Verifies that input "w" marks a more significant domain label than "www.".
2760 TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
2761 QueryForInput(ASCIIToUTF16("w"), false, false);
2762 SearchSuggestionParser::NavigationResult result(
2763 ChromeAutocompleteSchemeClassifier(&profile_),
2764 GURL("http://www.wow.com"), AutocompleteMatchType::NAVSUGGEST,
2765 base::string16(), std::string(), false, 0, false, ASCIIToUTF16("w"),
2766 std::string());
2767 result.set_received_after_last_keystroke(false);
2768 AutocompleteMatch match(provider_->NavigationToMatch(result));
2769 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
2770 EXPECT_TRUE(match.allowed_to_be_default_match);
2771 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
2772 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
2774 // Ensure that the match for input "w" is marked on "wow" and not "www".
2775 ASSERT_EQ(3U, match.contents_class.size());
2776 EXPECT_EQ(0U, match.contents_class[0].offset);
2777 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2778 match.contents_class[0].style);
2779 EXPECT_EQ(4U, match.contents_class[1].offset);
2780 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
2781 AutocompleteMatch::ACMatchClassification::MATCH,
2782 match.contents_class[1].style);
2783 EXPECT_EQ(5U, match.contents_class[2].offset);
2784 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
2785 match.contents_class[2].style);
2788 #if !defined(OS_WIN)
2789 // Verify entity suggestion parsing.
2790 TEST_F(SearchProviderTest, ParseEntitySuggestion) {
2791 struct Match {
2792 std::string contents;
2793 std::string description;
2794 std::string query_params;
2795 std::string fill_into_edit;
2796 AutocompleteMatchType::Type type;
2798 const Match kEmptyMatch = {
2799 kNotApplicable, kNotApplicable, kNotApplicable, kNotApplicable,
2800 AutocompleteMatchType::NUM_TYPES};
2802 struct {
2803 const std::string input_text;
2804 const std::string response_json;
2805 const Match matches[5];
2806 } cases[] = {
2807 // A query and an entity suggestion with different search terms.
2808 { "x",
2809 "[\"x\",[\"xy\", \"yy\"],[\"\",\"\"],[],"
2810 " {\"google:suggestdetail\":[{},"
2811 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
2812 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2813 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2814 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
2815 { "xy", "A", "p=v", "yy",
2816 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
2817 kEmptyMatch,
2818 kEmptyMatch
2821 // A query and an entity suggestion with same search terms.
2822 { "x",
2823 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
2824 " {\"google:suggestdetail\":[{},"
2825 " {\"a\":\"A\",\"t\":\"xy\",\"q\":\"p=v\"}],"
2826 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
2827 { { "x", "", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
2828 { "xy", "", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST },
2829 { "xy", "A", "p=v", "xy",
2830 AutocompleteMatchType::SEARCH_SUGGEST_ENTITY },
2831 kEmptyMatch,
2832 kEmptyMatch
2836 for (size_t i = 0; i < arraysize(cases); ++i) {
2837 QueryForInputAndWaitForFetcherResponses(
2838 ASCIIToUTF16(cases[i].input_text), false, cases[i].response_json,
2839 std::string());
2841 const ACMatches& matches = provider_->matches();
2842 ASSERT_FALSE(matches.empty());
2844 SCOPED_TRACE("for input with json = " + cases[i].response_json);
2846 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
2847 size_t j = 0;
2848 // Ensure that the returned matches equal the expectations.
2849 for (; j < matches.size(); ++j) {
2850 const Match& match = cases[i].matches[j];
2851 SCOPED_TRACE(" and match index: " + base::SizeTToString(j));
2852 EXPECT_EQ(match.contents,
2853 base::UTF16ToUTF8(matches[j].contents));
2854 EXPECT_EQ(match.description,
2855 base::UTF16ToUTF8(matches[j].description));
2856 EXPECT_EQ(match.query_params,
2857 matches[j].search_terms_args->suggest_query_params);
2858 EXPECT_EQ(match.fill_into_edit,
2859 base::UTF16ToUTF8(matches[j].fill_into_edit));
2860 EXPECT_EQ(match.type, matches[j].type);
2862 // Ensure that no expected matches are missing.
2863 for (; j < arraysize(cases[i].matches); ++j) {
2864 SCOPED_TRACE(" and match index: " + base::SizeTToString(j));
2865 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
2866 EXPECT_EQ(cases[i].matches[j].description, kNotApplicable);
2867 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
2868 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
2869 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
2873 #endif // !defined(OS_WIN)
2876 // A basic test that verifies the prefetch metadata parsing logic.
2877 TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
2878 struct Match {
2879 std::string contents;
2880 bool allowed_to_be_prefetched;
2881 AutocompleteMatchType::Type type;
2882 bool from_keyword;
2884 const Match kEmptyMatch = { kNotApplicable,
2885 false,
2886 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
2887 false };
2889 struct {
2890 const std::string input_text;
2891 bool prefer_keyword_provider_results;
2892 const std::string default_provider_response_json;
2893 const std::string keyword_provider_response_json;
2894 const Match matches[5];
2895 } cases[] = {
2896 // Default provider response does not have prefetch details. Ensure that the
2897 // suggestions are not marked as prefetch query.
2898 { "a",
2899 false,
2900 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2901 std::string(),
2902 { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2903 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2904 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2905 kEmptyMatch,
2906 kEmptyMatch
2909 // Ensure that default provider suggest response prefetch details are
2910 // parsed and recorded in AutocompleteMatch.
2911 { "ab",
2912 false,
2913 "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
2914 "{\"google:clientdata\":{\"phi\": 0},"
2915 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
2916 "\"google:suggestrelevance\":[999, 12, 1]}]",
2917 std::string(),
2918 { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2919 { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false },
2920 { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2921 { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2922 kEmptyMatch
2925 // Default provider suggest response has prefetch details.
2926 // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
2927 // the same query string. Ensure that the prefetch details from
2928 // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
2929 { "ab",
2930 false,
2931 "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
2932 "{\"google:clientdata\":{\"phi\": 0},"
2933 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2934 "\"google:suggestrelevance\":[99, 98]}]",
2935 std::string(),
2936 { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2937 {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
2938 kEmptyMatch,
2939 kEmptyMatch,
2940 kEmptyMatch
2943 // Default provider response has prefetch details. We prefer keyword
2944 // provider results. Ensure that prefetch bit for a suggestion from the
2945 // default search provider does not get copied onto a higher-scoring match
2946 // for the same query string from the keyword provider.
2947 { "k a",
2948 true,
2949 "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
2950 "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2951 "\"google:suggestrelevance\":[9, 12]}]",
2952 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
2953 { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
2954 { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
2955 { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
2956 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
2957 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
2962 for (size_t i = 0; i < arraysize(cases); ++i) {
2963 QueryForInputAndWaitForFetcherResponses(
2964 ASCIIToUTF16(cases[i].input_text),
2965 cases[i].prefer_keyword_provider_results,
2966 cases[i].default_provider_response_json,
2967 cases[i].prefer_keyword_provider_results ?
2968 cases[i].keyword_provider_response_json : std::string());
2970 const std::string description =
2971 "for input with json =" + cases[i].default_provider_response_json;
2972 const ACMatches& matches = provider_->matches();
2973 // The top match must inline and score as highly as calculated verbatim.
2974 ASSERT_FALSE(matches.empty());
2975 EXPECT_GE(matches[0].relevance, 1300);
2977 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
2978 // Ensure that the returned matches equal the expectations.
2979 for (size_t j = 0; j < matches.size(); ++j) {
2980 SCOPED_TRACE(description);
2981 EXPECT_EQ(cases[i].matches[j].contents,
2982 base::UTF16ToUTF8(matches[j].contents));
2983 EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
2984 SearchProvider::ShouldPrefetch(matches[j]));
2985 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
2986 EXPECT_EQ(cases[i].matches[j].from_keyword,
2987 matches[j].keyword == ASCIIToUTF16("k"));
2992 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_InvalidResponse) {
2993 ClearAllResults();
2995 std::string input_str("abc");
2996 QueryForInputAndWaitForFetcherResponses(
2997 ASCIIToUTF16(input_str), false, "this is a bad non-json response",
2998 std::string());
3000 const ACMatches& matches = provider_->matches();
3002 // Should have exactly one "search what you typed" match
3003 ASSERT_TRUE(matches.size() == 1);
3004 EXPECT_EQ(input_str, base::UTF16ToUTF8(matches[0].contents));
3005 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3006 matches[0].type);
3009 // A basic test that verifies that the XSSI guarded JSON response is parsed
3010 // correctly.
3011 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing_ValidResponses) {
3012 struct Match {
3013 std::string contents;
3014 AutocompleteMatchType::Type type;
3016 const Match kEmptyMatch = {
3017 kNotApplicable, AutocompleteMatchType::NUM_TYPES
3020 struct {
3021 const std::string input_text;
3022 const std::string default_provider_response_json;
3023 const Match matches[4];
3024 } cases[] = {
3025 // No XSSI guard.
3026 { "a",
3027 "[\"a\",[\"b\", \"c\"],[],[],"
3028 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3029 "\"google:suggestrelevance\":[1, 2]}]",
3030 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3031 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3032 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3033 kEmptyMatch,
3036 // Standard XSSI guard - )]}'\n.
3037 { "a",
3038 ")]}'\n[\"a\",[\"b\", \"c\"],[],[],"
3039 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3040 "\"google:suggestrelevance\":[1, 2]}]",
3041 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3042 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3043 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3044 kEmptyMatch,
3047 // Modified XSSI guard - contains "[".
3048 { "a",
3049 ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[],"
3050 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3051 "\"google:suggestrelevance\":[1, 2]}]",
3052 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3053 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3054 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3055 kEmptyMatch,
3060 for (size_t i = 0; i < arraysize(cases); ++i) {
3061 ClearAllResults();
3062 QueryForInputAndWaitForFetcherResponses(
3063 ASCIIToUTF16(cases[i].input_text), false,
3064 cases[i].default_provider_response_json, std::string());
3066 const ACMatches& matches = provider_->matches();
3067 // The top match must inline and score as highly as calculated verbatim.
3068 ASSERT_FALSE(matches.empty());
3069 EXPECT_GE(matches[0].relevance, 1300);
3071 SCOPED_TRACE("for case: " + base::SizeTToString(i));
3072 ASSERT_LE(matches.size(), arraysize(cases[i].matches));
3073 size_t j = 0;
3074 // Ensure that the returned matches equal the expectations.
3075 for (; j < matches.size(); ++j) {
3076 SCOPED_TRACE("and match: " + base::SizeTToString(j));
3077 EXPECT_EQ(cases[i].matches[j].contents,
3078 base::UTF16ToUTF8(matches[j].contents));
3079 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3081 for (; j < arraysize(cases[i].matches); ++j) {
3082 SCOPED_TRACE("and match: " + base::SizeTToString(j));
3083 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3084 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3089 // Test that deletion url gets set on an AutocompleteMatch when available for a
3090 // personalized query or a personalized URL.
3091 TEST_F(SearchProviderTest, ParseDeletionUrl) {
3092 struct Match {
3093 std::string contents;
3094 std::string deletion_url;
3095 AutocompleteMatchType::Type type;
3098 const Match kEmptyMatch = {
3099 kNotApplicable, std::string(), AutocompleteMatchType::NUM_TYPES
3102 const char* url[] = {
3103 "http://defaultturl/complete/deleteitems"
3104 "?delq=ab&client=chrome&deltok=xsrf124",
3105 "http://defaultturl/complete/deleteitems"
3106 "?delq=www.amazon.com&client=chrome&deltok=xsrf123",
3109 struct {
3110 const std::string input_text;
3111 const std::string response_json;
3112 const Match matches[5];
3113 } cases[] = {
3114 // A deletion URL on a personalized query should be reflected in the
3115 // resulting AutocompleteMatch.
3116 { "a",
3117 "[\"a\",[\"ab\", \"ac\",\"www.amazon.com\"],[],[],"
3118 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
3119 "\"PERSONALIZED_NAVIGATION\"],"
3120 "\"google:suggestrelevance\":[3, 2, 1],"
3121 "\"google:suggestdetail\":[{\"du\":"
3122 "\"/complete/deleteitems?delq=ab&client=chrome"
3123 "&deltok=xsrf124\"}, {}, {\"du\":"
3124 "\"/complete/deleteitems?delq=www.amazon.com&"
3125 "client=chrome&deltok=xsrf123\"}]}]",
3126 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3127 { "ab", url[0], AutocompleteMatchType::SEARCH_SUGGEST },
3128 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3129 { "www.amazon.com", url[1],
3130 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
3131 kEmptyMatch,
3134 // Personalized queries or a personalized URL without deletion URLs
3135 // shouldn't cause errors.
3136 { "a",
3137 "[\"a\",[\"ab\", \"ac\"],[],[],"
3138 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
3139 "\"PERSONALIZED_NAVIGATION\"],"
3140 "\"google:suggestrelevance\":[1, 2],"
3141 "\"google:suggestdetail\":[{}, {}]}]",
3142 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3143 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3144 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
3145 { "www.amazon.com", "",
3146 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
3147 kEmptyMatch,
3150 // Personalized queries or a personalized URL without
3151 // google:suggestdetail shouldn't cause errors.
3152 { "a",
3153 "[\"a\",[\"ab\", \"ac\"],[],[],"
3154 "{\"google:suggesttype\":[\"PERSONALIZED_QUERY\",\"QUERY\","
3155 "\"PERSONALIZED_NAVIGATION\"],"
3156 "\"google:suggestrelevance\":[1, 2]}]",
3157 { { "a", "", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3158 { "ac", "", AutocompleteMatchType::SEARCH_SUGGEST },
3159 { "ab", "", AutocompleteMatchType::SEARCH_SUGGEST },
3160 { "www.amazon.com", "",
3161 AutocompleteMatchType::NAVSUGGEST_PERSONALIZED },
3162 kEmptyMatch,
3167 for (size_t i = 0; i < arraysize(cases); ++i) {
3168 QueryForInputAndWaitForFetcherResponses(
3169 ASCIIToUTF16(cases[i].input_text), false, cases[i].response_json,
3170 std::string());
3172 const ACMatches& matches = provider_->matches();
3173 ASSERT_FALSE(matches.empty());
3175 SCOPED_TRACE("for input with json = " + cases[i].response_json);
3177 for (size_t j = 0; j < matches.size(); ++j) {
3178 const Match& match = cases[i].matches[j];
3179 SCOPED_TRACE(" and match index: " + base::SizeTToString(j));
3180 EXPECT_EQ(match.contents, base::UTF16ToUTF8(matches[j].contents));
3181 EXPECT_EQ(match.deletion_url, matches[j].GetAdditionalInfo(
3182 "deletion_url"));
3187 TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
3188 profile_.GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, false);
3189 base::string16 term = term1_.substr(0, term1_.length() - 1);
3190 QueryForInput(term, true, false);
3191 ASSERT_FALSE(provider_->matches().empty());
3192 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3193 provider_->matches()[0].type);
3194 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3195 EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3197 profile_.GetPrefs()->SetBoolean(bookmarks::prefs::kShowBookmarkBar, true);
3198 term = term1_.substr(0, term1_.length() - 1);
3199 QueryForInput(term, true, false);
3200 ASSERT_FALSE(provider_->matches().empty());
3201 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3202 provider_->matches()[0].type);
3203 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3204 EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3207 TEST_F(SearchProviderTest, CanSendURL) {
3208 TemplateURLData template_url_data;
3209 template_url_data.SetShortName(ASCIIToUTF16("t"));
3210 template_url_data.SetURL("http://www.google.com/{searchTerms}");
3211 template_url_data.suggestions_url = "http://www.google.com/{searchTerms}";
3212 template_url_data.instant_url = "http://does/not/exist?strk=1";
3213 template_url_data.search_terms_replacement_key = "strk";
3214 template_url_data.id = SEARCH_ENGINE_GOOGLE;
3215 TemplateURL google_template_url(template_url_data);
3217 // Create field trial.
3218 CreateFieldTrial(OmniboxFieldTrial::kZeroSuggestRule, true);
3220 ChromeAutocompleteProviderClient client(&profile_);
3222 // Not signed in.
3223 EXPECT_FALSE(SearchProvider::CanSendURL(
3224 GURL("http://www.google.com/search"),
3225 GURL("https://www.google.com/complete/search"), &google_template_url,
3226 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3227 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_);
3228 signin->SetAuthenticatedAccountInfo("gaia_id", "test");
3230 // All conditions should be met.
3231 EXPECT_TRUE(SearchProvider::CanSendURL(
3232 GURL("http://www.google.com/search"),
3233 GURL("https://www.google.com/complete/search"), &google_template_url,
3234 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3236 // Not in field trial.
3237 ResetFieldTrialList();
3238 CreateFieldTrial(OmniboxFieldTrial::kZeroSuggestRule, false);
3239 EXPECT_FALSE(SearchProvider::CanSendURL(
3240 GURL("http://www.google.com/search"),
3241 GURL("https://www.google.com/complete/search"), &google_template_url,
3242 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3243 ResetFieldTrialList();
3244 CreateFieldTrial(OmniboxFieldTrial::kZeroSuggestRule, true);
3246 // Invalid page URL.
3247 EXPECT_FALSE(SearchProvider::CanSendURL(
3248 GURL("badpageurl"),
3249 GURL("https://www.google.com/complete/search"), &google_template_url,
3250 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3252 // Invalid page classification.
3253 EXPECT_FALSE(SearchProvider::CanSendURL(
3254 GURL("http://www.google.com/search"),
3255 GURL("https://www.google.com/complete/search"), &google_template_url,
3256 metrics::OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
3257 SearchTermsData(), &client));
3259 // Invalid page classification.
3260 EXPECT_FALSE(SearchProvider::CanSendURL(
3261 GURL("http://www.google.com/search"),
3262 GURL("https://www.google.com/complete/search"), &google_template_url,
3263 metrics::OmniboxEventProto::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
3264 SearchTermsData(), &client));
3266 // HTTPS page URL on same domain as provider.
3267 EXPECT_TRUE(SearchProvider::CanSendURL(
3268 GURL("https://www.google.com/search"),
3269 GURL("https://www.google.com/complete/search"),
3270 &google_template_url, metrics::OmniboxEventProto::OTHER,
3271 SearchTermsData(), &client));
3273 // Non-HTTP[S] page URL on same domain as provider.
3274 EXPECT_FALSE(SearchProvider::CanSendURL(
3275 GURL("ftp://www.google.com/search"),
3276 GURL("https://www.google.com/complete/search"), &google_template_url,
3277 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3279 // Non-HTTP page URL on different domain.
3280 EXPECT_FALSE(SearchProvider::CanSendURL(
3281 GURL("https://www.notgoogle.com/search"),
3282 GURL("https://www.google.com/complete/search"), &google_template_url,
3283 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3285 // Non-HTTPS provider.
3286 EXPECT_FALSE(SearchProvider::CanSendURL(
3287 GURL("http://www.google.com/search"),
3288 GURL("http://www.google.com/complete/search"), &google_template_url,
3289 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3291 // Suggest disabled.
3292 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false);
3293 EXPECT_FALSE(SearchProvider::CanSendURL(
3294 GURL("http://www.google.com/search"),
3295 GURL("https://www.google.com/complete/search"), &google_template_url,
3296 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3297 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
3299 // Incognito.
3300 ChromeAutocompleteProviderClient client_incognito(
3301 profile_.GetOffTheRecordProfile());
3302 EXPECT_FALSE(SearchProvider::CanSendURL(
3303 GURL("http://www.google.com/search"),
3304 GURL("https://www.google.com/complete/search"), &google_template_url,
3305 metrics::OmniboxEventProto::OTHER, SearchTermsData(),
3306 &client_incognito));
3308 // Tab sync not enabled.
3309 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncKeepEverythingSynced,
3310 false);
3311 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, false);
3312 EXPECT_FALSE(SearchProvider::CanSendURL(
3313 GURL("http://www.google.com/search"),
3314 GURL("https://www.google.com/complete/search"), &google_template_url,
3315 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3316 profile_.GetPrefs()->SetBoolean(sync_driver::prefs::kSyncTabs, true);
3318 // Tab sync is encrypted.
3319 ProfileSyncService* service =
3320 ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_);
3321 syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes();
3322 encrypted_types.Put(syncer::SESSIONS);
3323 service->OnEncryptedTypesChanged(encrypted_types, false);
3324 EXPECT_FALSE(SearchProvider::CanSendURL(
3325 GURL("http://www.google.com/search"),
3326 GURL("https://www.google.com/complete/search"), &google_template_url,
3327 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3328 encrypted_types.Remove(syncer::SESSIONS);
3329 service->OnEncryptedTypesChanged(encrypted_types, false);
3331 // Check that there were no side effects from previous tests.
3332 EXPECT_TRUE(SearchProvider::CanSendURL(
3333 GURL("http://www.google.com/search"),
3334 GURL("https://www.google.com/complete/search"), &google_template_url,
3335 metrics::OmniboxEventProto::OTHER, SearchTermsData(), &client));
3338 TEST_F(SearchProviderTest, TestDeleteMatch) {
3339 AutocompleteMatch match(
3340 provider_.get(), 0, true, AutocompleteMatchType::SEARCH_SUGGEST);
3341 match.RecordAdditionalInfo(
3342 SearchProvider::kDeletionUrlKey,
3343 "https://www.google.com/complete/deleteitem?q=foo");
3345 // Test a successful deletion request.
3346 provider_->matches_.push_back(match);
3347 provider_->DeleteMatch(match);
3348 EXPECT_FALSE(provider_->deletion_handlers_.empty());
3349 EXPECT_TRUE(provider_->matches_.empty());
3350 // Set up a default fetcher with provided results.
3351 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3352 SearchProvider::kDeletionURLFetcherID);
3353 ASSERT_TRUE(fetcher);
3354 fetcher->set_response_code(200);
3355 fetcher->delegate()->OnURLFetchComplete(fetcher);
3356 EXPECT_TRUE(provider_->deletion_handlers_.empty());
3357 EXPECT_TRUE(provider_->is_success());
3359 // Test a failing deletion request.
3360 provider_->matches_.push_back(match);
3361 provider_->DeleteMatch(match);
3362 EXPECT_FALSE(provider_->deletion_handlers_.empty());
3363 // Set up a default fetcher with provided results.
3364 fetcher = test_factory_.GetFetcherByID(
3365 SearchProvider::kDeletionURLFetcherID);
3366 ASSERT_TRUE(fetcher);
3367 fetcher->set_response_code(500);
3368 fetcher->delegate()->OnURLFetchComplete(fetcher);
3369 EXPECT_TRUE(provider_->deletion_handlers_.empty());
3370 EXPECT_FALSE(provider_->is_success());
3373 TEST_F(SearchProviderTest, TestDeleteHistoryQueryMatch) {
3374 GURL term_url(
3375 AddSearchToHistory(default_t_url_, ASCIIToUTF16("flash games"), 1));
3376 profile_.BlockUntilHistoryProcessesPendingRequests();
3378 AutocompleteMatch games;
3379 QueryForInput(ASCIIToUTF16("fla"), false, false);
3380 profile_.BlockUntilHistoryProcessesPendingRequests();
3381 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
3382 ASSERT_TRUE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
3384 size_t matches_before = provider_->matches().size();
3385 provider_->DeleteMatch(games);
3386 EXPECT_EQ(matches_before - 1, provider_->matches().size());
3388 // Process history deletions.
3389 profile_.BlockUntilHistoryProcessesPendingRequests();
3391 // Check that the match is gone.
3392 QueryForInput(ASCIIToUTF16("fla"), false, false);
3393 profile_.BlockUntilHistoryProcessesPendingRequests();
3394 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
3395 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("flash games"), &games));
3398 // Verifies that duplicates are preserved in AddMatchToMap().
3399 TEST_F(SearchProviderTest, CheckDuplicateMatchesSaved) {
3400 AddSearchToHistory(default_t_url_, ASCIIToUTF16("a"), 1);
3401 AddSearchToHistory(default_t_url_, ASCIIToUTF16("alpha"), 1);
3402 AddSearchToHistory(default_t_url_, ASCIIToUTF16("avid"), 1);
3404 profile_.BlockUntilHistoryProcessesPendingRequests();
3405 QueryForInputAndWaitForFetcherResponses(
3406 ASCIIToUTF16("a"), false,
3407 "[\"a\",[\"a\", \"alpha\", \"avid\", \"apricot\"],[],[],"
3408 "{\"google:suggestrelevance\":[1450, 1200, 1150, 1100],"
3409 "\"google:verbatimrelevance\":1350}]",
3410 std::string());
3412 AutocompleteMatch verbatim, match_alpha, match_apricot, match_avid;
3413 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
3414 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("alpha"), &match_alpha));
3415 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("apricot"), &match_apricot));
3416 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("avid"), &match_avid));
3418 // Verbatim match duplicates are added such that each one has a higher
3419 // relevance than the previous one.
3420 EXPECT_EQ(2U, verbatim.duplicate_matches.size());
3422 // Other match duplicates are added in descending relevance order.
3423 EXPECT_EQ(1U, match_alpha.duplicate_matches.size());
3424 EXPECT_EQ(1U, match_avid.duplicate_matches.size());
3426 EXPECT_EQ(0U, match_apricot.duplicate_matches.size());
3429 TEST_F(SearchProviderTest, SuggestQueryUsesToken) {
3430 TemplateURLService* turl_model =
3431 TemplateURLServiceFactory::GetForProfile(&profile_);
3433 TemplateURLData data;
3434 data.SetShortName(ASCIIToUTF16("default"));
3435 data.SetKeyword(data.short_name());
3436 data.SetURL("http://example/{searchTerms}{google:sessionToken}");
3437 data.suggestions_url =
3438 "http://suggest/?q={searchTerms}&{google:sessionToken}";
3439 default_t_url_ = new TemplateURL(data);
3440 turl_model->Add(default_t_url_);
3441 turl_model->SetUserSelectedDefaultSearchProvider(default_t_url_);
3443 base::string16 term = term1_.substr(0, term1_.length() - 1);
3444 QueryForInput(term, false, false);
3446 // Make sure the default provider's suggest service was queried.
3447 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
3448 SearchProvider::kDefaultProviderURLFetcherID);
3449 ASSERT_TRUE(fetcher);
3451 // And the URL matches what we expected.
3452 TemplateURLRef::SearchTermsArgs search_terms_args(term);
3453 search_terms_args.session_token = provider_->current_token_;
3454 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
3455 search_terms_args, turl_model->search_terms_data()));
3456 EXPECT_EQ(fetcher->GetOriginalURL().spec(), expected_url.spec());
3458 // Complete running the fetcher to clean up.
3459 fetcher->set_response_code(200);
3460 fetcher->delegate()->OnURLFetchComplete(fetcher);
3461 RunTillProviderDone();
3464 TEST_F(SearchProviderTest, SessionToken) {
3465 // Subsequent calls always get the same token.
3466 std::string token = provider_->GetSessionToken();
3467 std::string token2 = provider_->GetSessionToken();
3468 EXPECT_EQ(token, token2);
3469 EXPECT_FALSE(token.empty());
3471 // Calls do not regenerate a token.
3472 provider_->current_token_ = "PRE-EXISTING TOKEN";
3473 token = provider_->GetSessionToken();
3474 EXPECT_EQ(token, "PRE-EXISTING TOKEN");
3476 // ... unless the token has expired.
3477 provider_->current_token_.clear();
3478 const base::TimeDelta kSmallDelta = base::TimeDelta::FromMilliseconds(1);
3479 provider_->token_expiration_time_ = base::TimeTicks::Now() - kSmallDelta;
3480 token = provider_->GetSessionToken();
3481 EXPECT_FALSE(token.empty());
3482 EXPECT_EQ(token, provider_->current_token_);
3484 // The expiration time is always updated.
3485 provider_->GetSessionToken();
3486 base::TimeTicks expiration_time_1 = provider_->token_expiration_time_;
3487 base::PlatformThread::Sleep(kSmallDelta);
3488 provider_->GetSessionToken();
3489 base::TimeTicks expiration_time_2 = provider_->token_expiration_time_;
3490 EXPECT_GT(expiration_time_2, expiration_time_1);
3491 EXPECT_GE(expiration_time_2, expiration_time_1 + kSmallDelta);
3494 TEST_F(SearchProviderTest, AnswersCache) {
3495 AutocompleteResult result;
3496 ACMatches matches;
3497 AutocompleteMatch match1;
3498 match1.answer_contents = base::ASCIIToUTF16("m1");
3499 match1.answer_type = base::ASCIIToUTF16("2334");
3500 match1.fill_into_edit = base::ASCIIToUTF16("weather los angeles");
3502 AutocompleteMatch non_answer_match1;
3503 non_answer_match1.fill_into_edit = base::ASCIIToUTF16("weather laguna beach");
3505 // Test that an answer in the first slot populates the cache.
3506 matches.push_back(match1);
3507 matches.push_back(non_answer_match1);
3508 result.AppendMatches(AutocompleteInput(), matches);
3509 provider_->RegisterDisplayedAnswers(result);
3510 ASSERT_FALSE(provider_->answers_cache_.empty());
3512 // Without scored results, no answers will be retrieved.
3513 AnswersQueryData answer = provider_->FindAnswersPrefetchData();
3514 EXPECT_TRUE(answer.full_query_text.empty());
3515 EXPECT_TRUE(answer.query_type.empty());
3517 // Inject a scored result, which will trigger answer retrieval.
3518 base::string16 query = base::ASCIIToUTF16("weather los angeles");
3519 SearchSuggestionParser::SuggestResult suggest_result(
3520 query, AutocompleteMatchType::SEARCH_HISTORY, query, base::string16(),
3521 base::string16(), base::string16(), base::string16(), nullptr,
3522 std::string(), std::string(), false, 1200, false, false, query);
3523 QueryForInput(ASCIIToUTF16("weather l"), false, false);
3524 provider_->transformed_default_history_results_.push_back(suggest_result);
3525 answer = provider_->FindAnswersPrefetchData();
3526 EXPECT_EQ(base::ASCIIToUTF16("weather los angeles"), answer.full_query_text);
3527 EXPECT_EQ(base::ASCIIToUTF16("2334"), answer.query_type);
3530 TEST_F(SearchProviderTest, RemoveExtraAnswers) {
3531 SuggestionAnswer answer1;
3532 answer1.set_type(42);
3533 SuggestionAnswer answer2;
3534 answer2.set_type(1983);
3535 SuggestionAnswer answer3;
3536 answer3.set_type(423);
3538 ACMatches matches;
3539 AutocompleteMatch match1, match2, match3, match4, match5;
3540 match1.answer = SuggestionAnswer::copy(&answer1);
3541 match1.answer_contents = base::ASCIIToUTF16("the answer");
3542 match1.answer_type = base::ASCIIToUTF16("42");
3543 match3.answer = SuggestionAnswer::copy(&answer2);
3544 match3.answer_contents = base::ASCIIToUTF16("not to play");
3545 match3.answer_type = base::ASCIIToUTF16("1983");
3546 match5.answer = SuggestionAnswer::copy(&answer3);
3547 match5.answer_contents = base::ASCIIToUTF16("a man");
3548 match5.answer_type = base::ASCIIToUTF16("423");
3550 matches.push_back(match1);
3551 matches.push_back(match2);
3552 matches.push_back(match3);
3553 matches.push_back(match4);
3554 matches.push_back(match5);
3556 SearchProvider::RemoveExtraAnswers(&matches);
3557 EXPECT_EQ(base::ASCIIToUTF16("the answer"), matches[0].answer_contents);
3558 EXPECT_EQ(base::ASCIIToUTF16("42"), matches[0].answer_type);
3559 EXPECT_TRUE(answer1.Equals(*matches[0].answer));
3560 EXPECT_TRUE(matches[1].answer_contents.empty());
3561 EXPECT_TRUE(matches[1].answer_type.empty());
3562 EXPECT_FALSE(matches[1].answer);
3563 EXPECT_TRUE(matches[2].answer_contents.empty());
3564 EXPECT_TRUE(matches[2].answer_type.empty());
3565 EXPECT_FALSE(matches[2].answer);
3566 EXPECT_TRUE(matches[3].answer_contents.empty());
3567 EXPECT_TRUE(matches[3].answer_type.empty());
3568 EXPECT_FALSE(matches[3].answer);
3569 EXPECT_TRUE(matches[4].answer_contents.empty());
3570 EXPECT_TRUE(matches[4].answer_type.empty());
3571 EXPECT_FALSE(matches[4].answer);
3574 TEST_F(SearchProviderTest, DoesNotProvideOnFocus) {
3575 AutocompleteInput input(
3576 base::ASCIIToUTF16("f"), base::string16::npos, std::string(), GURL(),
3577 metrics::OmniboxEventProto::INVALID_SPEC, false, true, true, true, true,
3578 ChromeAutocompleteSchemeClassifier(&profile_));
3579 provider_->Start(input, false);
3580 EXPECT_TRUE(provider_->matches().empty());