Omnibox: Loosen SearchProvider Constraints for Reorder Mode
[chromium-blink-merge.git] / chrome / browser / autocomplete / search_provider_unittest.cc
blobeafbe745b31b034b08e23aba3a52ac6e8feb8338
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 "chrome/browser/autocomplete/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/autocomplete_controller.h"
21 #include "chrome/browser/autocomplete/autocomplete_input.h"
22 #include "chrome/browser/autocomplete/autocomplete_match.h"
23 #include "chrome/browser/autocomplete/autocomplete_provider.h"
24 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h"
25 #include "chrome/browser/autocomplete/history_url_provider.h"
26 #include "chrome/browser/history/history_service.h"
27 #include "chrome/browser/history/history_service_factory.h"
28 #include "chrome/browser/omnibox/omnibox_field_trial.h"
29 #include "chrome/browser/search/search.h"
30 #include "chrome/browser/search_engines/search_engine_type.h"
31 #include "chrome/browser/search_engines/template_url.h"
32 #include "chrome/browser/search_engines/template_url_service.h"
33 #include "chrome/browser/search_engines/template_url_service_factory.h"
34 #include "chrome/browser/signin/signin_manager.h"
35 #include "chrome/browser/signin/signin_manager_factory.h"
36 #include "chrome/browser/sync/profile_sync_service.h"
37 #include "chrome/browser/sync/profile_sync_service_factory.h"
38 #include "chrome/common/chrome_switches.h"
39 #include "chrome/common/metrics/variations/variations_util.h"
40 #include "chrome/common/pref_names.h"
41 #include "chrome/test/base/testing_browser_process.h"
42 #include "chrome/test/base/testing_profile.h"
43 #include "components/variations/entropy_provider.h"
44 #include "content/public/test/test_browser_thread_bundle.h"
45 #include "net/url_request/test_url_fetcher_factory.h"
46 #include "net/url_request/url_request_status.h"
47 #include "testing/gtest/include/gtest/gtest.h"
49 // SearchProviderTest ---------------------------------------------------------
51 // The following environment is configured for these tests:
52 // . The TemplateURL default_t_url_ is set as the default provider.
53 // . The TemplateURL keyword_t_url_ is added to the TemplateURLService. This
54 // TemplateURL has a valid suggest and search URL.
55 // . The URL created by using the search term term1_ with default_t_url_ is
56 // added to history.
57 // . The URL created by using the search term keyword_term_ with keyword_t_url_
58 // is added to history.
59 // . test_factory_ is set as the URLFetcherFactory.
60 class SearchProviderTest : public testing::Test,
61 public AutocompleteProviderListener {
62 public:
63 struct ResultInfo {
64 ResultInfo() : result_type(AutocompleteMatchType::NUM_TYPES) {
66 ResultInfo(GURL gurl,
67 AutocompleteMatch::Type result_type,
68 string16 fill_into_edit)
69 : gurl(gurl),
70 result_type(result_type),
71 fill_into_edit(fill_into_edit) {
74 const GURL gurl;
75 const AutocompleteMatch::Type result_type;
76 const string16 fill_into_edit;
79 struct TestData {
80 const string16 input;
81 const size_t num_results;
82 const ResultInfo output[3];
85 SearchProviderTest()
86 : default_t_url_(NULL),
87 term1_(ASCIIToUTF16("term1")),
88 keyword_t_url_(NULL),
89 keyword_term_(ASCIIToUTF16("keyword")),
90 run_loop_(NULL) {
91 ResetFieldTrialList();
94 // See description above class for what this registers.
95 virtual void SetUp() OVERRIDE;
96 virtual void TearDown() OVERRIDE;
98 void RunTest(TestData* cases, int num_cases, bool prefer_keyword);
100 protected:
101 // Needed for AutocompleteFieldTrial::ActivateStaticTrials();
102 scoped_ptr<base::FieldTrialList> field_trial_list_;
104 // Default value used for testing.
105 static const std::string kNotApplicable;
107 // Adds a search for |term|, using the engine |t_url| to the history, and
108 // returns the URL for that search.
109 GURL AddSearchToHistory(TemplateURL* t_url, string16 term, int visit_count);
111 // Looks for a match in |provider_| with |contents| equal to |contents|.
112 // Sets |match| to it if found. Returns whether |match| was set.
113 bool FindMatchWithContents(const string16& contents,
114 AutocompleteMatch* match);
116 // Looks for a match in |provider_| with destination |url|. Sets |match| to
117 // it if found. Returns whether |match| was set.
118 bool FindMatchWithDestination(const GURL& url, AutocompleteMatch* match);
120 // AutocompleteProviderListener:
121 // If we're waiting for the provider to finish, this exits the message loop.
122 virtual void OnProviderUpdate(bool updated_matches) OVERRIDE;
124 // Runs a nested message loop until provider_ is done. The message loop is
125 // exited by way of OnProviderUpdate.
126 void RunTillProviderDone();
128 // Invokes Start on provider_, then runs all pending tasks.
129 void QueryForInput(const string16& text,
130 bool prevent_inline_autocomplete,
131 bool prefer_keyword);
133 // Calls QueryForInput(), finishes any suggest query, then if |wyt_match| is
134 // non-NULL, sets it to the "what you typed" entry for |text|.
135 void QueryForInputAndSetWYTMatch(const string16& text,
136 AutocompleteMatch* wyt_match);
138 // Notifies the URLFetcher for the suggest query corresponding to the default
139 // search provider that it's done.
140 // Be sure and wrap calls to this in ASSERT_NO_FATAL_FAILURE.
141 void FinishDefaultSuggestQuery();
143 // Runs SearchProvider on |input|, for which the suggest server replies
144 // with |json|, and expects that the resulting matches' contents equals
145 // that in |matches|. An empty entry in |matches| means no match should
146 // be returned in that position. Reports any errors with a message that
147 // includes |error_description|.
148 void ForcedQueryTestHelper(const std::string& input,
149 const std::string& json,
150 const std::string matches[3],
151 const std::string& error_description);
153 void ResetFieldTrialList();
155 void ClearAllResults();
157 // See description above class for details of these fields.
158 TemplateURL* default_t_url_;
159 const string16 term1_;
160 GURL term1_url_;
161 TemplateURL* keyword_t_url_;
162 const string16 keyword_term_;
163 GURL keyword_url_;
165 content::TestBrowserThreadBundle thread_bundle_;
167 // URLFetcherFactory implementation registered.
168 net::TestURLFetcherFactory test_factory_;
170 // Profile we use.
171 TestingProfile profile_;
173 // The provider.
174 scoped_refptr<SearchProvider> provider_;
176 // If non-NULL, OnProviderUpdate quits the current |run_loop_|.
177 base::RunLoop* run_loop_;
179 DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
182 // static
183 const std::string SearchProviderTest::kNotApplicable = "Not Applicable";
185 void SearchProviderTest::SetUp() {
186 // Make sure that fetchers are automatically ungregistered upon destruction.
187 test_factory_.set_remove_fetcher_on_delete(true);
189 // We need both the history service and template url model loaded.
190 ASSERT_TRUE(profile_.CreateHistoryService(true, false));
191 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse(
192 &profile_, &TemplateURLServiceFactory::BuildInstanceFor);
194 TemplateURLService* turl_model =
195 TemplateURLServiceFactory::GetForProfile(&profile_);
197 turl_model->Load();
199 // Reset the default TemplateURL.
200 TemplateURLData data;
201 data.short_name = ASCIIToUTF16("t");
202 data.SetURL("http://defaultturl/{searchTerms}");
203 data.suggestions_url = "http://defaultturl2/{searchTerms}";
204 data.instant_url = "http://does/not/exist?strk=1";
205 data.search_terms_replacement_key = "strk";
206 default_t_url_ = new TemplateURL(&profile_, data);
207 turl_model->Add(default_t_url_);
208 turl_model->SetDefaultSearchProvider(default_t_url_);
209 TemplateURLID default_provider_id = default_t_url_->id();
210 ASSERT_NE(0, default_provider_id);
212 // Add url1, with search term term1_.
213 term1_url_ = AddSearchToHistory(default_t_url_, term1_, 1);
215 // Create another TemplateURL.
216 data.short_name = ASCIIToUTF16("k");
217 data.SetKeyword(ASCIIToUTF16("k"));
218 data.SetURL("http://keyword/{searchTerms}");
219 data.suggestions_url = "http://suggest_keyword/{searchTerms}";
220 keyword_t_url_ = new TemplateURL(&profile_, data);
221 turl_model->Add(keyword_t_url_);
222 ASSERT_NE(0, keyword_t_url_->id());
224 // Add a page and search term for keyword_t_url_.
225 keyword_url_ = AddSearchToHistory(keyword_t_url_, keyword_term_, 1);
227 // Keywords are updated by the InMemoryHistoryBackend only after the message
228 // has been processed on the history thread. Block until history processes all
229 // requests to ensure the InMemoryDatabase is the state we expect it.
230 profile_.BlockUntilHistoryProcessesPendingRequests();
232 provider_ = new SearchProvider(this, &profile_);
233 provider_->kMinimumTimeBetweenSuggestQueriesMs = 0;
236 void SearchProviderTest::TearDown() {
237 base::RunLoop().RunUntilIdle();
239 // Shutdown the provider before the profile.
240 provider_ = NULL;
243 void SearchProviderTest::RunTest(TestData* cases,
244 int num_cases,
245 bool prefer_keyword) {
246 ACMatches matches;
247 for (int i = 0; i < num_cases; ++i) {
248 AutocompleteInput input(cases[i].input, string16::npos, string16(), GURL(),
249 AutocompleteInput::INVALID_SPEC, false,
250 prefer_keyword, true,
251 AutocompleteInput::ALL_MATCHES);
252 provider_->Start(input, false);
253 matches = provider_->matches();
254 string16 diagnostic_details = ASCIIToUTF16("Input was: ") + cases[i].input +
255 ASCIIToUTF16("; prefer_keyword was: ") +
256 (prefer_keyword ? ASCIIToUTF16("true") : ASCIIToUTF16("false"));
257 EXPECT_EQ(cases[i].num_results, matches.size()) << diagnostic_details;
258 if (matches.size() == cases[i].num_results) {
259 for (size_t j = 0; j < cases[i].num_results; ++j) {
260 EXPECT_EQ(cases[i].output[j].gurl, matches[j].destination_url) <<
261 diagnostic_details;
262 EXPECT_EQ(cases[i].output[j].result_type, matches[j].type) <<
263 diagnostic_details;
264 EXPECT_EQ(cases[i].output[j].fill_into_edit,
265 matches[j].fill_into_edit) <<
266 diagnostic_details;
267 // All callers that use this helper function at the moment produce
268 // matches that are always allowed to be the default match.
269 EXPECT_TRUE(matches[j].allowed_to_be_default_match);
275 void SearchProviderTest::OnProviderUpdate(bool updated_matches) {
276 if (run_loop_ && provider_->done()) {
277 run_loop_->Quit();
278 run_loop_ = NULL;
282 void SearchProviderTest::RunTillProviderDone() {
283 if (provider_->done())
284 return;
286 base::RunLoop run_loop;
287 run_loop_ = &run_loop;
288 run_loop.Run();
291 void SearchProviderTest::QueryForInput(const string16& text,
292 bool prevent_inline_autocomplete,
293 bool prefer_keyword) {
294 // Start a query.
295 AutocompleteInput input(text, string16::npos, string16(), GURL(),
296 AutocompleteInput::INVALID_SPEC,
297 prevent_inline_autocomplete, prefer_keyword, true,
298 AutocompleteInput::ALL_MATCHES);
299 provider_->Start(input, false);
301 // RunUntilIdle so that the task scheduled by SearchProvider to create the
302 // URLFetchers runs.
303 base::RunLoop().RunUntilIdle();
306 void SearchProviderTest::QueryForInputAndSetWYTMatch(
307 const string16& text,
308 AutocompleteMatch* wyt_match) {
309 QueryForInput(text, false, false);
310 profile_.BlockUntilHistoryProcessesPendingRequests();
311 ASSERT_NO_FATAL_FAILURE(FinishDefaultSuggestQuery());
312 if (!wyt_match)
313 return;
314 ASSERT_GE(provider_->matches().size(), 1u);
315 EXPECT_TRUE(FindMatchWithDestination(GURL(
316 default_t_url_->url_ref().ReplaceSearchTerms(
317 TemplateURLRef::SearchTermsArgs(text))),
318 wyt_match));
321 GURL SearchProviderTest::AddSearchToHistory(TemplateURL* t_url,
322 string16 term,
323 int visit_count) {
324 HistoryService* history =
325 HistoryServiceFactory::GetForProfile(&profile_,
326 Profile::EXPLICIT_ACCESS);
327 GURL search(t_url->url_ref().ReplaceSearchTerms(
328 TemplateURLRef::SearchTermsArgs(term)));
329 static base::Time last_added_time;
330 last_added_time = std::max(base::Time::Now(),
331 last_added_time + base::TimeDelta::FromMicroseconds(1));
332 history->AddPageWithDetails(search, string16(), visit_count, visit_count,
333 last_added_time, false, history::SOURCE_BROWSED);
334 history->SetKeywordSearchTermsForURL(search, t_url->id(), term);
335 return search;
338 bool SearchProviderTest::FindMatchWithContents(const string16& contents,
339 AutocompleteMatch* match) {
340 for (ACMatches::const_iterator i = provider_->matches().begin();
341 i != provider_->matches().end(); ++i) {
342 if (i->contents == contents) {
343 *match = *i;
344 return true;
347 return false;
350 bool SearchProviderTest::FindMatchWithDestination(const GURL& url,
351 AutocompleteMatch* match) {
352 for (ACMatches::const_iterator i = provider_->matches().begin();
353 i != provider_->matches().end(); ++i) {
354 if (i->destination_url == url) {
355 *match = *i;
356 return true;
359 return false;
362 void SearchProviderTest::FinishDefaultSuggestQuery() {
363 net::TestURLFetcher* default_fetcher =
364 test_factory_.GetFetcherByID(
365 SearchProvider::kDefaultProviderURLFetcherID);
366 ASSERT_TRUE(default_fetcher);
368 // Tell the SearchProvider the default suggest query is done.
369 default_fetcher->set_response_code(200);
370 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
373 void SearchProviderTest::ForcedQueryTestHelper(
374 const std::string& input,
375 const std::string& json,
376 const std::string expected_matches[3],
377 const std::string& error_description) {
378 QueryForInput(ASCIIToUTF16(input), false, false);
379 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
380 SearchProvider::kDefaultProviderURLFetcherID);
381 ASSERT_TRUE(fetcher);
382 fetcher->set_response_code(200);
383 fetcher->SetResponseString(json);
384 fetcher->delegate()->OnURLFetchComplete(fetcher);
385 RunTillProviderDone();
387 const ACMatches& matches = provider_->matches();
388 ASSERT_LE(matches.size(), 3u);
389 size_t i = 0;
390 // Ensure that the returned matches equal the expectations.
391 for (; i < matches.size(); ++i) {
392 EXPECT_EQ(ASCIIToUTF16(expected_matches[i]), matches[i].contents) <<
393 error_description;
395 // Ensure that no expected matches are missing.
396 for (; i < 3u; ++i) {
397 EXPECT_EQ(std::string(), expected_matches[i]) <<
398 "Case #" << i << ": " << error_description;
402 void SearchProviderTest::ResetFieldTrialList() {
403 // Destroy the existing FieldTrialList before creating a new one to avoid
404 // a DCHECK.
405 field_trial_list_.reset();
406 field_trial_list_.reset(new base::FieldTrialList(
407 new metrics::SHA1EntropyProvider("foo")));
408 chrome_variations::testing::ClearAllVariationParams();
409 base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
410 "AutocompleteDynamicTrial_0", "DefaultGroup");
411 trial->group();
414 void SearchProviderTest::ClearAllResults() {
415 provider_->ClearAllResults();
418 // Actual Tests ---------------------------------------------------------------
420 // Make sure we query history for the default provider and a URLFetcher is
421 // created for the default provider suggest results.
422 TEST_F(SearchProviderTest, QueryDefaultProvider) {
423 string16 term = term1_.substr(0, term1_.length() - 1);
424 QueryForInput(term, false, false);
426 // Make sure the default providers suggest service was queried.
427 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
428 SearchProvider::kDefaultProviderURLFetcherID);
429 ASSERT_TRUE(fetcher);
431 // And the URL matches what we expected.
432 GURL expected_url(default_t_url_->suggestions_url_ref().ReplaceSearchTerms(
433 TemplateURLRef::SearchTermsArgs(term)));
434 ASSERT_TRUE(fetcher->GetOriginalURL() == expected_url);
436 // Tell the SearchProvider the suggest query is done.
437 fetcher->set_response_code(200);
438 fetcher->delegate()->OnURLFetchComplete(fetcher);
439 fetcher = NULL;
441 // Run till the history results complete.
442 RunTillProviderDone();
444 // The SearchProvider is done. Make sure it has a result for the history
445 // term term1.
446 AutocompleteMatch term1_match;
447 EXPECT_TRUE(FindMatchWithDestination(term1_url_, &term1_match));
448 // Term1 should not have a description, it's set later.
449 EXPECT_TRUE(term1_match.description.empty());
451 AutocompleteMatch wyt_match;
452 EXPECT_TRUE(FindMatchWithDestination(
453 GURL(default_t_url_->url_ref().ReplaceSearchTerms(
454 TemplateURLRef::SearchTermsArgs(term))), &wyt_match));
455 EXPECT_TRUE(wyt_match.description.empty());
457 // The match for term1 should be more relevant than the what you typed match.
458 EXPECT_GT(term1_match.relevance, wyt_match.relevance);
459 // This longer match should be inlineable.
460 EXPECT_TRUE(term1_match.allowed_to_be_default_match);
461 // The what you typed match should be too, of course.
462 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
465 TEST_F(SearchProviderTest, HonorPreventInlineAutocomplete) {
466 string16 term = term1_.substr(0, term1_.length() - 1);
467 QueryForInput(term, true, false);
469 ASSERT_FALSE(provider_->matches().empty());
470 ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
471 provider_->matches()[0].type);
472 EXPECT_TRUE(provider_->matches()[0].allowed_to_be_default_match);
475 // Issues a query that matches the registered keyword and makes sure history
476 // is queried as well as URLFetchers getting created.
477 TEST_F(SearchProviderTest, QueryKeywordProvider) {
478 string16 term = keyword_term_.substr(0, keyword_term_.length() - 1);
479 QueryForInput(keyword_t_url_->keyword() + ASCIIToUTF16(" ") + term,
480 false,
481 false);
483 // Make sure the default providers suggest service was queried.
484 net::TestURLFetcher* default_fetcher = test_factory_.GetFetcherByID(
485 SearchProvider::kDefaultProviderURLFetcherID);
486 ASSERT_TRUE(default_fetcher);
488 // Tell the SearchProvider the default suggest query is done.
489 default_fetcher->set_response_code(200);
490 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
491 default_fetcher = NULL;
493 // Make sure the keyword providers suggest service was queried.
494 net::TestURLFetcher* keyword_fetcher = test_factory_.GetFetcherByID(
495 SearchProvider::kKeywordProviderURLFetcherID);
496 ASSERT_TRUE(keyword_fetcher);
498 // And the URL matches what we expected.
499 GURL expected_url(keyword_t_url_->suggestions_url_ref().ReplaceSearchTerms(
500 TemplateURLRef::SearchTermsArgs(term)));
501 ASSERT_TRUE(keyword_fetcher->GetOriginalURL() == expected_url);
503 // Tell the SearchProvider the keyword suggest query is done.
504 keyword_fetcher->set_response_code(200);
505 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
506 keyword_fetcher = NULL;
508 // Run till the history results complete.
509 RunTillProviderDone();
511 // The SearchProvider is done. Make sure it has a result for the history
512 // term keyword.
513 AutocompleteMatch match;
514 EXPECT_TRUE(FindMatchWithDestination(keyword_url_, &match));
516 // The match should have an associated keyword.
517 EXPECT_FALSE(match.keyword.empty());
519 // The fill into edit should contain the keyword.
520 EXPECT_EQ(keyword_t_url_->keyword() + char16(' ') + keyword_term_,
521 match.fill_into_edit);
524 TEST_F(SearchProviderTest, DontSendPrivateDataToSuggest) {
525 // None of the following input strings should be sent to the suggest server,
526 // because they may contain private data.
527 const char* inputs[] = {
528 "username:password",
529 "http://username:password",
530 "https://username:password",
531 "username:password@hostname",
532 "http://username:password@hostname/",
533 "file://filename",
534 "data://data",
535 "unknownscheme:anything",
536 "http://hostname/?query=q",
537 "http://hostname/path#ref",
538 "http://hostname/path #ref",
539 "https://hostname/path",
542 for (size_t i = 0; i < arraysize(inputs); ++i) {
543 QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
544 // Make sure the default provider's suggest service was not queried.
545 ASSERT_TRUE(test_factory_.GetFetcherByID(
546 SearchProvider::kDefaultProviderURLFetcherID) == NULL);
547 // Run till the history results complete.
548 RunTillProviderDone();
552 TEST_F(SearchProviderTest, SendNonPrivateDataToSuggest) {
553 // All of the following input strings should be sent to the suggest server,
554 // because they should not get caught by the private data checks.
555 const char* inputs[] = {
556 "query",
557 "query with spaces",
558 "http://hostname",
559 "http://hostname/path",
560 "http://hostname #ref",
561 "www.hostname.com #ref",
562 "https://hostname",
563 "#hashtag",
564 "foo https://hostname/path"
567 profile_.BlockUntilHistoryProcessesPendingRequests();
568 for (size_t i = 0; i < arraysize(inputs); ++i) {
569 QueryForInput(ASCIIToUTF16(inputs[i]), false, false);
570 // Make sure the default provider's suggest service was queried.
571 ASSERT_TRUE(test_factory_.GetFetcherByID(
572 SearchProvider::kDefaultProviderURLFetcherID) != NULL);
576 TEST_F(SearchProviderTest, DontAutocompleteURLLikeTerms) {
577 AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
578 &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
579 GURL url = AddSearchToHistory(default_t_url_,
580 ASCIIToUTF16("docs.google.com"), 1);
582 // Add the term as a url.
583 HistoryServiceFactory::GetForProfile(&profile_, Profile::EXPLICIT_ACCESS)->
584 AddPageWithDetails(GURL("http://docs.google.com"), string16(), 1, 1,
585 base::Time::Now(), false, history::SOURCE_BROWSED);
586 profile_.BlockUntilHistoryProcessesPendingRequests();
588 AutocompleteMatch wyt_match;
589 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("docs"),
590 &wyt_match));
592 // There should be two matches, one for what you typed, the other for
593 // 'docs.google.com'. The search term should have a lower priority than the
594 // what you typed match.
595 ASSERT_EQ(2u, provider_->matches().size());
596 AutocompleteMatch term_match;
597 EXPECT_TRUE(FindMatchWithDestination(url, &term_match));
598 EXPECT_GT(wyt_match.relevance, term_match.relevance);
599 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
600 EXPECT_TRUE(term_match.allowed_to_be_default_match);
603 TEST_F(SearchProviderTest, DontGiveNavsuggestionsInForcedQueryMode) {
604 const std::string kEmptyMatch;
605 struct {
606 const std::string json;
607 const std::string matches_in_default_mode[3];
608 const std::string matches_in_forced_query_mode[3];
609 } cases[] = {
610 // Without suggested relevance scores.
611 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
612 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"]}]",
613 { "a", "a1.com", "a2" },
614 { "a", "a2", kEmptyMatch } },
616 // With suggested relevance scores in a situation where navsuggest would
617 // go second.
618 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
619 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
620 "\"google:suggestrelevance\":[1250, 1200]}]",
621 { "a", "a1.com", "a2" },
622 { "a", "a2", kEmptyMatch } },
624 // With suggested relevance scores in a situation where navsuggest
625 // would go first.
626 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
627 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
628 "\"google:suggestrelevance\":[1350, 1250]}]",
629 { "a1.com", "a", "a2" },
630 { "a", "a2", kEmptyMatch } },
632 // With suggested relevance scores in a situation where navsuggest
633 // would go first only because verbatim has been demoted.
634 { "[\"a\",[\"http://a1.com\", \"a2\"],[],[],"
635 "{\"google:suggesttype\":[\"NAVIGATION\", \"QUERY\"],"
636 "\"google:suggestrelevance\":[1450, 1400],"
637 "\"google:verbatimrelevance\":1350}]",
638 { "a1.com", "a2", "a" },
639 { "a2", "a", kEmptyMatch } },
642 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
643 ForcedQueryTestHelper("a", cases[i].json, cases[i].matches_in_default_mode,
644 "regular input with json=" + cases[i].json);
645 ForcedQueryTestHelper("?a", cases[i].json,
646 cases[i].matches_in_forced_query_mode,
647 "forced query input with json=" + cases[i].json);
651 // A multiword search with one visit should not autocomplete until multiple
652 // words are typed.
653 TEST_F(SearchProviderTest, DontAutocompleteUntilMultipleWordsTyped) {
654 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("one search"),
655 1));
656 profile_.BlockUntilHistoryProcessesPendingRequests();
658 AutocompleteMatch wyt_match;
659 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("on"),
660 &wyt_match));
661 ASSERT_EQ(2u, provider_->matches().size());
662 AutocompleteMatch term_match;
663 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
664 EXPECT_GT(wyt_match.relevance, term_match.relevance);
665 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
666 EXPECT_TRUE(term_match.allowed_to_be_default_match);
668 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("one se"),
669 &wyt_match));
670 ASSERT_EQ(2u, provider_->matches().size());
671 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
672 EXPECT_GT(term_match.relevance, wyt_match.relevance);
673 EXPECT_TRUE(term_match.allowed_to_be_default_match);
674 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
677 // A multiword search with more than one visit should autocomplete immediately.
678 TEST_F(SearchProviderTest, AutocompleteMultipleVisitsImmediately) {
679 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
680 2));
681 profile_.BlockUntilHistoryProcessesPendingRequests();
683 AutocompleteMatch wyt_match;
684 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("tw"),
685 &wyt_match));
686 ASSERT_EQ(2u, provider_->matches().size());
687 AutocompleteMatch term_match;
688 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
689 EXPECT_GT(term_match.relevance, wyt_match.relevance);
690 EXPECT_TRUE(term_match.allowed_to_be_default_match);
691 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
694 // Autocompletion should work at a word boundary after a space.
695 TEST_F(SearchProviderTest, AutocompleteAfterSpace) {
696 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("two searches"),
697 2));
698 profile_.BlockUntilHistoryProcessesPendingRequests();
700 AutocompleteMatch wyt_match;
701 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("two "),
702 &wyt_match));
703 ASSERT_EQ(2u, provider_->matches().size());
704 AutocompleteMatch term_match;
705 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
706 EXPECT_GT(term_match.relevance, wyt_match.relevance);
707 EXPECT_TRUE(term_match.allowed_to_be_default_match);
708 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
711 // Newer multiword searches should score more highly than older ones.
712 TEST_F(SearchProviderTest, ScoreNewerSearchesHigher) {
713 GURL term_url_a(AddSearchToHistory(default_t_url_,
714 ASCIIToUTF16("three searches aaa"), 1));
715 GURL term_url_b(AddSearchToHistory(default_t_url_,
716 ASCIIToUTF16("three searches bbb"), 1));
717 profile_.BlockUntilHistoryProcessesPendingRequests();
719 AutocompleteMatch wyt_match;
720 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("three se"),
721 &wyt_match));
722 ASSERT_EQ(3u, provider_->matches().size());
723 AutocompleteMatch term_match_a;
724 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
725 AutocompleteMatch term_match_b;
726 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
727 EXPECT_GT(term_match_b.relevance, term_match_a.relevance);
728 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
729 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
730 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
731 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
734 // An autocompleted multiword search should not be replaced by a different
735 // autocompletion while the user is still typing a valid prefix.
736 TEST_F(SearchProviderTest, DontReplacePreviousAutocompletion) {
737 GURL term_url_a(AddSearchToHistory(default_t_url_,
738 ASCIIToUTF16("four searches aaa"), 2));
739 GURL term_url_b(AddSearchToHistory(default_t_url_,
740 ASCIIToUTF16("four searches bbb"), 1));
741 profile_.BlockUntilHistoryProcessesPendingRequests();
743 AutocompleteMatch wyt_match;
744 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fo"),
745 &wyt_match));
746 ASSERT_EQ(3u, provider_->matches().size());
747 AutocompleteMatch term_match_a;
748 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
749 AutocompleteMatch term_match_b;
750 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
751 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
752 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
753 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
754 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
755 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
757 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("four se"),
758 &wyt_match));
759 ASSERT_EQ(3u, provider_->matches().size());
760 EXPECT_TRUE(FindMatchWithDestination(term_url_a, &term_match_a));
761 EXPECT_TRUE(FindMatchWithDestination(term_url_b, &term_match_b));
762 EXPECT_GT(term_match_a.relevance, wyt_match.relevance);
763 EXPECT_GT(wyt_match.relevance, term_match_b.relevance);
764 EXPECT_TRUE(term_match_a.allowed_to_be_default_match);
765 EXPECT_TRUE(term_match_b.allowed_to_be_default_match);
766 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
769 // Non-completable multiword searches should not crowd out single-word searches.
770 TEST_F(SearchProviderTest, DontCrowdOutSingleWords) {
771 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("five"), 1));
772 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches bbb"), 1);
773 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ccc"), 1);
774 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches ddd"), 1);
775 AddSearchToHistory(default_t_url_, ASCIIToUTF16("five searches eee"), 1);
776 profile_.BlockUntilHistoryProcessesPendingRequests();
778 AutocompleteMatch wyt_match;
779 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("fi"),
780 &wyt_match));
781 ASSERT_EQ(AutocompleteProvider::kMaxMatches + 1, provider_->matches().size());
782 AutocompleteMatch term_match;
783 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
784 EXPECT_GT(term_match.relevance, wyt_match.relevance);
785 EXPECT_TRUE(term_match.allowed_to_be_default_match);
786 EXPECT_TRUE(wyt_match.allowed_to_be_default_match);
789 // Inline autocomplete matches regardless of case differences from the input.
790 TEST_F(SearchProviderTest, InlineMixedCaseMatches) {
791 GURL term_url(AddSearchToHistory(default_t_url_, ASCIIToUTF16("FOO"), 1));
792 profile_.BlockUntilHistoryProcessesPendingRequests();
794 AutocompleteMatch wyt_match;
795 ASSERT_NO_FATAL_FAILURE(QueryForInputAndSetWYTMatch(ASCIIToUTF16("f"),
796 &wyt_match));
797 ASSERT_EQ(2u, provider_->matches().size());
798 AutocompleteMatch term_match;
799 EXPECT_TRUE(FindMatchWithDestination(term_url, &term_match));
800 EXPECT_GT(term_match.relevance, wyt_match.relevance);
801 EXPECT_EQ(ASCIIToUTF16("FOO"), term_match.fill_into_edit);
802 EXPECT_EQ(ASCIIToUTF16("OO"), term_match.inline_autocompletion);
803 EXPECT_TRUE(term_match.allowed_to_be_default_match);
806 // Verifies AutocompleteControllers return results (including keyword
807 // results) in the right order and set descriptions for them correctly.
808 TEST_F(SearchProviderTest, KeywordOrderingAndDescriptions) {
809 // Add an entry that corresponds to a keyword search with 'term2'.
810 AddSearchToHistory(keyword_t_url_, ASCIIToUTF16("term2"), 1);
811 profile_.BlockUntilHistoryProcessesPendingRequests();
813 AutocompleteController controller(&profile_, NULL,
814 AutocompleteProvider::TYPE_SEARCH);
815 controller.Start(AutocompleteInput(
816 ASCIIToUTF16("k t"), string16::npos, string16(), GURL(),
817 AutocompleteInput::INVALID_SPEC, false, false, true,
818 AutocompleteInput::ALL_MATCHES));
819 const AutocompleteResult& result = controller.result();
821 // There should be three matches, one for the keyword history, one for
822 // keyword provider's what-you-typed, and one for the default provider's
823 // what you typed, in that order.
824 ASSERT_EQ(3u, result.size());
825 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, result.match_at(0).type);
826 EXPECT_EQ(AutocompleteMatchType::SEARCH_OTHER_ENGINE,
827 result.match_at(1).type);
828 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
829 result.match_at(2).type);
830 EXPECT_GT(result.match_at(0).relevance, result.match_at(1).relevance);
831 EXPECT_GT(result.match_at(1).relevance, result.match_at(2).relevance);
832 EXPECT_TRUE(result.match_at(0).allowed_to_be_default_match);
833 EXPECT_TRUE(result.match_at(1).allowed_to_be_default_match);
834 EXPECT_TRUE(result.match_at(2).allowed_to_be_default_match);
836 // The two keyword results should come with the keyword we expect.
837 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(0).keyword);
838 EXPECT_EQ(ASCIIToUTF16("k"), result.match_at(1).keyword);
839 // The default provider has a different keyword. (We don't explicitly
840 // set it during this test, so all we do is assert that it's different.)
841 EXPECT_NE(result.match_at(0).keyword, result.match_at(2).keyword);
843 // The top result will always have a description. The third result,
844 // coming from a different provider than the first two, should also.
845 // Whether the second result has one doesn't matter much. (If it was
846 // missing, people would infer that it's the same search provider as
847 // the one above it.)
848 EXPECT_FALSE(result.match_at(0).description.empty());
849 EXPECT_FALSE(result.match_at(2).description.empty());
850 EXPECT_NE(result.match_at(0).description, result.match_at(2).description);
853 TEST_F(SearchProviderTest, KeywordVerbatim) {
854 TestData cases[] = {
855 // Test a simple keyword input.
856 { ASCIIToUTF16("k foo"), 2,
857 { ResultInfo(GURL("http://keyword/foo"),
858 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
859 ASCIIToUTF16("k foo")),
860 ResultInfo(GURL("http://defaultturl/k%20foo"),
861 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
862 ASCIIToUTF16("k foo") ) } },
864 // Make sure extra whitespace after the keyword doesn't change the
865 // keyword verbatim query.
866 { ASCIIToUTF16("k foo"), 2,
867 { ResultInfo(GURL("http://keyword/foo"),
868 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
869 ASCIIToUTF16("k foo")),
870 ResultInfo(GURL("http://defaultturl/k%20%20%20foo"),
871 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
872 ASCIIToUTF16("k foo")) } },
873 // Leading whitespace should be stripped before SearchProvider gets the
874 // input; hence there are no tests here about how it handles those inputs.
876 // But whitespace elsewhere in the query string should matter to both
877 // matches.
878 { ASCIIToUTF16("k foo bar"), 2,
879 { ResultInfo(GURL("http://keyword/foo%20%20bar"),
880 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
881 ASCIIToUTF16("k foo bar")),
882 ResultInfo(GURL("http://defaultturl/k%20%20foo%20%20bar"),
883 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
884 ASCIIToUTF16("k foo bar")) } },
885 // Note in the above test case we don't test trailing whitespace because
886 // SearchProvider still doesn't handle this well. See related bugs:
887 // 102690, 99239, 164635.
889 // Keywords can be prefixed by certain things that should get ignored
890 // when constructing the keyword match.
891 { ASCIIToUTF16("www.k foo"), 2,
892 { ResultInfo(GURL("http://keyword/foo"),
893 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
894 ASCIIToUTF16("k foo")),
895 ResultInfo(GURL("http://defaultturl/www.k%20foo"),
896 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
897 ASCIIToUTF16("www.k foo")) } },
898 { ASCIIToUTF16("http://k foo"), 2,
899 { ResultInfo(GURL("http://keyword/foo"),
900 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
901 ASCIIToUTF16("k foo")),
902 ResultInfo(GURL("http://defaultturl/http%3A//k%20foo"),
903 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
904 ASCIIToUTF16("http://k foo")) } },
905 { ASCIIToUTF16("http://www.k foo"), 2,
906 { ResultInfo(GURL("http://keyword/foo"),
907 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
908 ASCIIToUTF16("k foo")),
909 ResultInfo(GURL("http://defaultturl/http%3A//www.k%20foo"),
910 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
911 ASCIIToUTF16("http://www.k foo")) } },
913 // A keyword with no remaining input shouldn't get a keyword
914 // verbatim match.
915 { ASCIIToUTF16("k"), 1,
916 { ResultInfo(GURL("http://defaultturl/k"),
917 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
918 ASCIIToUTF16("k")) } },
919 { ASCIIToUTF16("k "), 1,
920 { ResultInfo(GURL("http://defaultturl/k%20"),
921 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
922 ASCIIToUTF16("k ")) } }
924 // The fact that verbatim queries to keyword are handled by KeywordProvider
925 // not SearchProvider is tested in
926 // chrome/browser/extensions/api/omnibox/omnibox_apitest.cc.
929 // Test not in keyword mode.
930 RunTest(cases, arraysize(cases), false);
932 // Test in keyword mode. (Both modes should give the same result.)
933 RunTest(cases, arraysize(cases), true);
936 // Ensures command-line flags are reflected in the URLs the search provider
937 // generates.
938 TEST_F(SearchProviderTest, CommandLineOverrides) {
939 TemplateURLService* turl_model =
940 TemplateURLServiceFactory::GetForProfile(&profile_);
942 TemplateURLData data;
943 data.short_name = ASCIIToUTF16("default");
944 data.SetKeyword(data.short_name);
945 data.SetURL("{google:baseURL}{searchTerms}");
946 default_t_url_ = new TemplateURL(&profile_, data);
947 turl_model->Add(default_t_url_);
948 turl_model->SetDefaultSearchProvider(default_t_url_);
950 CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kGoogleBaseURL,
951 "http://www.bar.com/");
952 CommandLine::ForCurrentProcess()->AppendSwitchASCII(
953 switches::kExtraSearchQueryParams, "a=b");
955 TestData cases[] = {
956 { ASCIIToUTF16("k a"), 2,
957 { ResultInfo(GURL("http://keyword/a"),
958 AutocompleteMatchType::SEARCH_OTHER_ENGINE,
959 ASCIIToUTF16("k a")),
960 ResultInfo(GURL("http://www.bar.com/k%20a?a=b"),
961 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
962 ASCIIToUTF16("k a")) } },
965 RunTest(cases, arraysize(cases), false);
968 // Verifies Navsuggest results don't set a TemplateURL, which Instant relies on.
969 // Also verifies that just the *first* navigational result is listed as a match
970 // if suggested relevance scores were not sent.
971 TEST_F(SearchProviderTest, NavSuggestNoSuggestedRelevanceScores) {
972 QueryForInput(ASCIIToUTF16("a.c"), false, false);
974 // Make sure the default providers suggest service was queried.
975 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
976 SearchProvider::kDefaultProviderURLFetcherID);
977 ASSERT_TRUE(fetcher);
979 // Tell the SearchProvider the suggest query is done.
980 fetcher->set_response_code(200);
981 fetcher->SetResponseString(
982 "[\"a.c\",[\"a.com\", \"a.com/b\"],[\"a\", \"b\"],[],"
983 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]");
984 fetcher->delegate()->OnURLFetchComplete(fetcher);
985 fetcher = NULL;
987 // Run till the history results complete.
988 RunTillProviderDone();
990 // Make sure the only match is 'a.com' and it doesn't have a template_url.
991 AutocompleteMatch nav_match;
992 EXPECT_TRUE(FindMatchWithDestination(GURL("http://a.com"), &nav_match));
993 EXPECT_TRUE(nav_match.keyword.empty());
994 EXPECT_TRUE(nav_match.allowed_to_be_default_match);
995 EXPECT_FALSE(FindMatchWithDestination(GURL("http://a.com/b"), &nav_match));
998 // Verifies that the most relevant suggest results are added properly.
999 TEST_F(SearchProviderTest, SuggestRelevance) {
1000 QueryForInput(ASCIIToUTF16("a"), false, false);
1002 // Make sure the default provider's suggest service was queried.
1003 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
1004 SearchProvider::kDefaultProviderURLFetcherID);
1005 ASSERT_TRUE(fetcher);
1007 // Tell the SearchProvider the suggest query is done.
1008 fetcher->set_response_code(200);
1009 fetcher->SetResponseString("[\"a\",[\"a1\", \"a2\", \"a3\", \"a4\"]]");
1010 fetcher->delegate()->OnURLFetchComplete(fetcher);
1011 fetcher = NULL;
1013 // Run till the history results complete.
1014 RunTillProviderDone();
1016 // Check the expected verbatim and (first 3) suggestions' relative relevances.
1017 AutocompleteMatch verbatim, match_a1, match_a2, match_a3, match_a4;
1018 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a"), &verbatim));
1019 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a1"), &match_a1));
1020 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a2"), &match_a2));
1021 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("a3"), &match_a3));
1022 EXPECT_FALSE(FindMatchWithContents(ASCIIToUTF16("a4"), &match_a4));
1023 EXPECT_GT(verbatim.relevance, match_a1.relevance);
1024 EXPECT_GT(match_a1.relevance, match_a2.relevance);
1025 EXPECT_GT(match_a2.relevance, match_a3.relevance);
1026 EXPECT_TRUE(verbatim.allowed_to_be_default_match);
1027 EXPECT_TRUE(match_a1.allowed_to_be_default_match);
1028 EXPECT_TRUE(match_a2.allowed_to_be_default_match);
1029 EXPECT_TRUE(match_a3.allowed_to_be_default_match);
1032 // Verifies that suggest results with relevance scores are added
1033 // properly when using the default fetcher. When adding a new test
1034 // case to this test, please consider adding it to the tests in
1035 // KeywordFetcherSuggestRelevance below.
1036 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevance) {
1037 struct DefaultFetcherMatch {
1038 std::string contents;
1039 bool allowed_to_be_default_match;
1041 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1042 struct {
1043 const std::string json;
1044 const DefaultFetcherMatch matches[4];
1045 const std::string inline_autocompletion;
1046 } cases[] = {
1047 // Ensure that suggestrelevance scores reorder matches.
1048 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1049 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch },
1050 std::string() },
1051 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1052 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1053 "\"google:suggestrelevance\":[1, 2]}]",
1054 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch },
1055 std::string() },
1057 // Without suggested relevance scores, we should only allow one
1058 // navsuggest result to be be displayed.
1059 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1060 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1061 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1062 std::string() },
1064 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1065 // Negative values will have no effect; the calculated value will be used.
1066 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1067 "\"google:suggestrelevance\":[9998]}]",
1068 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch },
1069 std::string() },
1070 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1071 "\"google:suggestrelevance\":[9999]}]",
1072 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1073 "1" },
1074 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1075 "\"google:suggestrelevance\":[9999]}]",
1076 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1077 "1" },
1078 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1079 "\"google:suggestrelevance\":[9999]}]",
1080 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1081 "1" },
1082 { "[\"a\",[\"http://a.com\"],[],[],"
1083 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1084 "\"google:verbatimrelevance\":9999,"
1085 "\"google:suggestrelevance\":[9998]}]",
1086 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
1087 std::string() },
1088 { "[\"a\",[\"http://a.com\"],[],[],"
1089 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1090 "\"google:verbatimrelevance\":9998,"
1091 "\"google:suggestrelevance\":[9999]}]",
1092 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1093 ".com" },
1094 { "[\"a\",[\"http://a.com\"],[],[],"
1095 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1096 "\"google:verbatimrelevance\":0,"
1097 "\"google:suggestrelevance\":[9999]}]",
1098 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1099 ".com" },
1100 { "[\"a\",[\"http://a.com\"],[],[],"
1101 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1102 "\"google:verbatimrelevance\":-1,"
1103 "\"google:suggestrelevance\":[9999]}]",
1104 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1105 ".com" },
1107 // Ensure that both types of relevance scores reorder matches together.
1108 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1109 "\"google:verbatimrelevance\":9998}]",
1110 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch },
1111 "1" },
1113 // Ensure that only inlinable matches may be ranked as the highest result.
1114 // Ignore all suggested relevance scores if this constraint is violated.
1115 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1116 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
1117 std::string() },
1118 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1119 "\"google:verbatimrelevance\":0}]",
1120 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
1121 std::string() },
1122 { "[\"a\",[\"http://b.com\"],[],[],"
1123 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1124 "\"google:suggestrelevance\":[9999]}]",
1125 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1126 std::string() },
1127 { "[\"a\",[\"http://b.com\"],[],[],"
1128 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1129 "\"google:suggestrelevance\":[9999],"
1130 "\"google:verbatimrelevance\":0}]",
1131 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1132 std::string() },
1133 { "[\"a\",[\"https://a/\"],[],[],"
1134 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1135 "\"google:suggestrelevance\":[9999]}]",
1136 { { "https://a", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1137 std::string() },
1139 // Ensure that the top result is ranked as highly as calculated verbatim.
1140 // Ignore the suggested verbatim relevance if this constraint is violated.
1141 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1142 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1143 std::string() },
1144 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1145 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1146 std::string() },
1147 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1148 "\"google:verbatimrelevance\":0}]",
1149 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1150 std::string() },
1151 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1152 "\"google:verbatimrelevance\":0}]",
1153 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
1154 std::string() },
1155 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1156 "\"google:verbatimrelevance\":2}]",
1157 { { "a", true }, { "a2", true }, { "a1", true }, kEmptyMatch },
1158 std::string() },
1159 { "[\"a\",[\"http://a.com\"],[],[],"
1160 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1161 "\"google:suggestrelevance\":[1],"
1162 "\"google:verbatimrelevance\":0}]",
1163 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
1164 std::string() },
1165 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1166 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1167 "\"google:suggestrelevance\":[1, 2],"
1168 "\"google:verbatimrelevance\":0}]",
1169 { { "a", true }, { "a2.com", true }, { "a1.com", true }, kEmptyMatch },
1170 std::string() },
1172 // Ensure that all suggestions are considered, regardless of order.
1173 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1174 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1175 { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
1176 std::string() },
1177 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1178 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1179 "\"http://h.com\"],[],[],"
1180 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1181 "\"NAVIGATION\", \"NAVIGATION\","
1182 "\"NAVIGATION\", \"NAVIGATION\","
1183 "\"NAVIGATION\"],"
1184 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1185 { { "a", true }, { "h.com", false }, { "g.com", false },
1186 { "f.com", false } },
1187 std::string() },
1189 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1190 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1191 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
1192 std::string() },
1193 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1194 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1195 std::string() },
1196 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1197 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1198 "\"google:suggestrelevance\":[1]}]",
1199 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1200 std::string() },
1201 { "[\"a\",[\"http://a1.com\"],[],[],"
1202 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1203 "\"google:suggestrelevance\":[9999, 1]}]",
1204 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1205 std::string() },
1207 // Ensure that all 'verbatim' results are merged with their maximum score.
1208 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1209 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1210 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1211 "2" },
1212 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1213 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1214 "\"google:verbatimrelevance\":0}]",
1215 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1216 "2" },
1218 // Ensure that verbatim is always generated without other suggestions.
1219 // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1220 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1221 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1222 std::string() },
1223 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1224 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1225 std::string() },
1228 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1229 QueryForInput(ASCIIToUTF16("a"), false, false);
1230 net::TestURLFetcher* fetcher =
1231 test_factory_.GetFetcherByID(
1232 SearchProvider::kDefaultProviderURLFetcherID);
1233 ASSERT_TRUE(fetcher);
1234 fetcher->set_response_code(200);
1235 fetcher->SetResponseString(cases[i].json);
1236 fetcher->delegate()->OnURLFetchComplete(fetcher);
1237 RunTillProviderDone();
1239 const std::string description = "for input with json=" + cases[i].json;
1240 const ACMatches& matches = provider_->matches();
1241 // The top match must inline and score as highly as calculated verbatim.
1242 ASSERT_FALSE(matches.empty());
1243 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1244 matches[0].inline_autocompletion) << description;
1245 EXPECT_GE(matches[0].relevance, 1300) << description;
1247 size_t j = 0;
1248 // Ensure that the returned matches equal the expectations.
1249 for (; j < matches.size(); ++j) {
1250 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1251 matches[j].contents) << description;
1252 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1253 matches[j].allowed_to_be_default_match) << description;
1255 // Ensure that no expected matches are missing.
1256 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1257 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1258 "Case # " << i << " " << description;
1262 // This test is like DefaultFetcherSuggestRelevance above except it enables
1263 // the field trial that causes the omnibox to be willing to reorder matches
1264 // to guarantee the top result is a legal default match. This field trial
1265 // causes SearchProvider to allow some constraints to be violated that it
1266 // wouldn't normally because the omnibox will fix the problems later.
1267 TEST_F(SearchProviderTest, DefaultFetcherSuggestRelevanceWithReorder) {
1268 struct DefaultFetcherMatch {
1269 std::string contents;
1270 bool allowed_to_be_default_match;
1272 const DefaultFetcherMatch kEmptyMatch = { kNotApplicable, false };
1273 struct {
1274 const std::string json;
1275 const DefaultFetcherMatch matches[4];
1276 const std::string inline_autocompletion;
1277 } cases[] = {
1278 // Ensure that suggestrelevance scores reorder matches.
1279 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1280 { { "a", true }, { "c", false }, { "b", false }, kEmptyMatch },
1281 std::string() },
1282 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1283 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1284 "\"google:suggestrelevance\":[1, 2]}]",
1285 { { "a", true }, { "c.com", false }, { "b.com", false }, kEmptyMatch },
1286 std::string() },
1288 // Without suggested relevance scores, we should only allow one
1289 // navsuggest result to be be displayed.
1290 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1291 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1292 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1293 std::string() },
1295 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1296 // Negative values will have no effect; the calculated value will be used.
1297 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1298 "\"google:suggestrelevance\":[9998]}]",
1299 { { "a", true}, { "a1", true }, kEmptyMatch, kEmptyMatch },
1300 std::string() },
1301 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1302 "\"google:suggestrelevance\":[9999]}]",
1303 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1304 "1" },
1305 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1306 "\"google:suggestrelevance\":[9999]}]",
1307 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1308 "1" },
1309 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1310 "\"google:suggestrelevance\":[9999]}]",
1311 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1312 "1" },
1313 { "[\"a\",[\"http://a.com\"],[],[],"
1314 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1315 "\"google:verbatimrelevance\":9999,"
1316 "\"google:suggestrelevance\":[9998]}]",
1317 { { "a", true }, { "a.com", true }, kEmptyMatch, kEmptyMatch },
1318 std::string() },
1319 { "[\"a\",[\"http://a.com\"],[],[],"
1320 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1321 "\"google:verbatimrelevance\":9998,"
1322 "\"google:suggestrelevance\":[9999]}]",
1323 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1324 ".com" },
1325 { "[\"a\",[\"http://a.com\"],[],[],"
1326 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1327 "\"google:verbatimrelevance\":0,"
1328 "\"google:suggestrelevance\":[9999]}]",
1329 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1330 ".com" },
1331 { "[\"a\",[\"http://a.com\"],[],[],"
1332 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1333 "\"google:verbatimrelevance\":-1,"
1334 "\"google:suggestrelevance\":[9999]}]",
1335 { { "a.com", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1336 ".com" },
1338 // Ensure that both types of relevance scores reorder matches together.
1339 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1340 "\"google:verbatimrelevance\":9998}]",
1341 { { "a1", true }, { "a", true }, { "a2", true }, kEmptyMatch },
1342 "1" },
1344 // Allow non-inlineable matches to be the highest-scoring match but,
1345 // if the result set lacks a single inlineable result, abandon suggested
1346 // relevance scores entirely.
1347 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1348 { { "b", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
1349 std::string() },
1350 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1351 "\"google:verbatimrelevance\":0}]",
1352 { { "a", true }, { "b", false }, kEmptyMatch, kEmptyMatch },
1353 std::string() },
1354 { "[\"a\",[\"http://b.com\"],[],[],"
1355 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1356 "\"google:suggestrelevance\":[9999]}]",
1357 { { "b.com", false }, { "a", true }, kEmptyMatch, kEmptyMatch },
1358 std::string() },
1359 { "[\"a\",[\"http://b.com\"],[],[],"
1360 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1361 "\"google:suggestrelevance\":[9999],"
1362 "\"google:verbatimrelevance\":0}]",
1363 { { "a", true }, { "b.com", false }, kEmptyMatch, kEmptyMatch },
1364 std::string() },
1366 // Allow low-scoring matches.
1367 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1368 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1369 "1" },
1370 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1371 { { "a1", true }, { "a", true }, kEmptyMatch, kEmptyMatch },
1372 "1" },
1373 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1374 "\"google:verbatimrelevance\":0}]",
1375 { { "a1", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1376 "1" },
1377 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1378 "\"google:verbatimrelevance\":0}]",
1379 { { "a2", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1380 "2" },
1381 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1382 "\"google:verbatimrelevance\":2}]",
1383 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1384 "2" },
1385 { "[\"a\",[\"http://a.com\"],[],[],"
1386 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1387 "\"google:suggestrelevance\":[1],"
1388 "\"google:verbatimrelevance\":0}]",
1389 { { "a.com", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1390 ".com" },
1391 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1392 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1393 "\"google:suggestrelevance\":[1, 2],"
1394 "\"google:verbatimrelevance\":0}]",
1395 { { "a2.com", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1396 "2.com" },
1398 // Ensure that all suggestions are considered, regardless of order.
1399 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1400 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1401 { { "a", true }, { "h", false }, { "g", false }, { "f", false } },
1402 std::string() },
1403 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1404 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1405 "\"http://h.com\"],[],[],"
1406 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1407 "\"NAVIGATION\", \"NAVIGATION\","
1408 "\"NAVIGATION\", \"NAVIGATION\","
1409 "\"NAVIGATION\"],"
1410 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1411 { { "a", true }, { "h.com", false }, { "g.com", false },
1412 { "f.com", false } },
1413 std::string() },
1415 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1416 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1417 { { "a", true }, { "a1", true }, { "a2", true }, kEmptyMatch },
1418 std::string() },
1419 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1420 { { "a", true }, { "a1", true }, kEmptyMatch, kEmptyMatch },
1421 std::string() },
1422 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1423 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1424 "\"google:suggestrelevance\":[1]}]",
1425 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1426 std::string() },
1427 { "[\"a\",[\"http://a1.com\"],[],[],"
1428 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1429 "\"google:suggestrelevance\":[9999, 1]}]",
1430 { { "a", true }, { "a1.com", true }, kEmptyMatch, kEmptyMatch },
1431 std::string() },
1433 // Ensure that all 'verbatim' results are merged with their maximum score.
1434 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1435 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1436 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1437 "2" },
1438 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1439 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1440 "\"google:verbatimrelevance\":0}]",
1441 { { "a2", true }, { "a", true }, { "a1", true }, kEmptyMatch },
1442 "2" },
1444 // Ensure that verbatim is always generated without other suggestions.
1445 // TODO(msw): Ensure verbatimrelevance is respected (except suppression).
1446 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1447 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1448 std::string() },
1449 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1450 { { "a", true }, kEmptyMatch, kEmptyMatch, kEmptyMatch },
1451 std::string() },
1454 std::map<std::string, std::string> params;
1455 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
1456 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled;
1457 ASSERT_TRUE(chrome_variations::AssociateVariationParams(
1458 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
1459 base::FieldTrialList::CreateFieldTrial(
1460 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
1462 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1463 QueryForInput(ASCIIToUTF16("a"), false, false);
1464 net::TestURLFetcher* fetcher =
1465 test_factory_.GetFetcherByID(
1466 SearchProvider::kDefaultProviderURLFetcherID);
1467 ASSERT_TRUE(fetcher);
1468 fetcher->set_response_code(200);
1469 fetcher->SetResponseString(cases[i].json);
1470 fetcher->delegate()->OnURLFetchComplete(fetcher);
1471 RunTillProviderDone();
1473 const std::string description = "for input with json=" + cases[i].json;
1474 const ACMatches& matches = provider_->matches();
1475 // The top match must inline and score as highly as calculated verbatim.
1476 ASSERT_FALSE(matches.empty());
1477 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1478 matches[0].inline_autocompletion) << description;
1480 size_t j = 0;
1481 // Ensure that the returned matches equal the expectations.
1482 for (; j < matches.size(); ++j) {
1483 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1484 matches[j].contents) << description;
1485 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1486 matches[j].allowed_to_be_default_match) << description;
1488 // Ensure that no expected matches are missing.
1489 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1490 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1491 "Case # " << i << " " << description;
1495 // Verifies that suggest results with relevance scores are added
1496 // properly when using the keyword fetcher. This is similar to the
1497 // test DefaultFetcherSuggestRelevance above but this uses inputs that
1498 // trigger keyword suggestions (i.e., "k a" rather than "a") and has
1499 // different expectations (because now the results are a mix of
1500 // keyword suggestions and default provider suggestions). When a new
1501 // test is added to this TEST_F, please consider if it would be
1502 // appropriate to add to DefaultFetcherSuggestRelevance as well.
1503 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevance) {
1504 struct KeywordFetcherMatch {
1505 std::string contents;
1506 bool from_keyword;
1507 bool allowed_to_be_default_match;
1509 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1510 struct {
1511 const std::string json;
1512 const KeywordFetcherMatch matches[5];
1513 const std::string inline_autocompletion;
1514 } cases[] = {
1515 // Ensure that suggest relevance scores reorder matches and that
1516 // the keyword verbatim (lacking a suggested verbatim score) beats
1517 // the default provider verbatim.
1518 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1519 { { "a", true, true },
1520 { "k a", false, true },
1521 { "c", true, false },
1522 { "b", true, false },
1523 kEmptyMatch },
1524 std::string() },
1525 // Again, check that relevance scores reorder matches, just this
1526 // time with navigation matches. This also checks that with
1527 // suggested relevance scores we allow multiple navsuggest results.
1528 // It's odd that navsuggest results that come from a keyword
1529 // provider are marked as not a keyword result. I think this
1530 // comes from them not going to a keyword search engine).
1531 // TODO(mpearson): Investigate the implications (if any) of
1532 // tagging these results appropriately. If so, do it because it
1533 // makes more sense.
1534 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
1535 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1536 "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
1537 { { "a", true, true },
1538 { "d", true, false },
1539 { "c.com", false, false },
1540 { "b.com", false, false },
1541 { "k a", false, true }, },
1542 std::string() },
1544 // Without suggested relevance scores, we should only allow one
1545 // navsuggest result to be be displayed.
1546 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
1547 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
1548 { { "a", true, true },
1549 { "b.com", false, false },
1550 { "k a", false, true },
1551 kEmptyMatch, kEmptyMatch },
1552 std::string() },
1554 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
1555 // Negative values will have no effect; the calculated value will be used.
1556 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
1557 "\"google:suggestrelevance\":[9998]}]",
1558 { { "a", true, true },
1559 { "a1", true, true },
1560 { "k a", false, true },
1561 kEmptyMatch, kEmptyMatch },
1562 std::string() },
1563 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
1564 "\"google:suggestrelevance\":[9999]}]",
1565 { { "a1", true, true },
1566 { "a", true, true },
1567 { "k a", false, true },
1568 kEmptyMatch, kEmptyMatch },
1569 "1" },
1570 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
1571 "\"google:suggestrelevance\":[9999]}]",
1572 { { "a1", true, true },
1573 { "k a", false, true },
1574 kEmptyMatch, kEmptyMatch, kEmptyMatch },
1575 "1" },
1576 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
1577 "\"google:suggestrelevance\":[9999]}]",
1578 { { "a1", true, true },
1579 { "a", true, true },
1580 { "k a", false, true },
1581 kEmptyMatch, kEmptyMatch },
1582 "1" },
1583 { "[\"a\",[\"http://a.com\"],[],[],"
1584 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1585 "\"google:verbatimrelevance\":9999,"
1586 "\"google:suggestrelevance\":[9998]}]",
1587 { { "a", true, true },
1588 { "a.com", false, true },
1589 { "k a", false, true },
1590 kEmptyMatch, kEmptyMatch },
1591 std::string() },
1593 // Ensure that both types of relevance scores reorder matches together.
1594 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
1595 "\"google:verbatimrelevance\":9998}]",
1596 { { "a1", true, true },
1597 { "a", true, true },
1598 { "a2", true, true },
1599 { "k a", false, true },
1600 kEmptyMatch },
1601 "1" },
1603 // Ensure that only inlinable matches may be ranked as the highest result.
1604 // Ignore all suggested relevance scores if this constraint is violated.
1605 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
1606 { { "a", true, true },
1607 { "b", true, false },
1608 { "k a", false, true },
1609 kEmptyMatch, kEmptyMatch },
1610 std::string() },
1611 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
1612 "\"google:verbatimrelevance\":0}]",
1613 { { "a", true, true },
1614 { "b", true, false },
1615 { "k a", false, true },
1616 kEmptyMatch, kEmptyMatch },
1617 std::string() },
1618 { "[\"a\",[\"http://b.com\"],[],[],"
1619 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1620 "\"google:suggestrelevance\":[9999]}]",
1621 { { "a", true, true },
1622 { "b.com", false, false },
1623 { "k a", false, true },
1624 kEmptyMatch, kEmptyMatch },
1625 std::string() },
1626 { "[\"a\",[\"http://b.com\"],[],[],"
1627 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1628 "\"google:suggestrelevance\":[9999],"
1629 "\"google:verbatimrelevance\":0}]",
1630 { { "a", true, true },
1631 { "b.com", false, false },
1632 { "k a", false, true },
1633 kEmptyMatch, kEmptyMatch },
1634 std::string() },
1636 // Ensure that the top result is ranked as highly as calculated verbatim.
1637 // Ignore the suggested verbatim relevance if this constraint is violated.
1638 // Note that keyword suggestions by default (not in suggested relevance
1639 // mode) score more highly than the default verbatim.
1640 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
1641 { { "a", true, true },
1642 { "a1", true, true },
1643 { "k a", false, true },
1644 kEmptyMatch, kEmptyMatch },
1645 std::string() },
1646 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
1647 { { "a", true, true },
1648 { "a1", true, true },
1649 { "k a", false, true },
1650 kEmptyMatch, kEmptyMatch},
1651 std::string() },
1652 // Continuing the same category of tests, but make sure we keep the
1653 // suggested relevance scores even as we discard the verbatim relevance
1654 // scores.
1655 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
1656 "\"google:verbatimrelevance\":0}]",
1657 { { "a", true, true },
1658 { "k a", false, true },
1659 { "a1", true, true },
1660 kEmptyMatch, kEmptyMatch},
1661 std::string() },
1662 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
1663 "\"google:verbatimrelevance\":0}]",
1664 { { "a", true, true },
1665 { "k a", false, true },
1666 { "a2", true, true },
1667 { "a1", true, true },
1668 kEmptyMatch },
1669 std::string() },
1670 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
1671 "\"google:verbatimrelevance\":2}]",
1672 { { "a", true, true },
1673 { "k a", false, true },
1674 { "a2", true, true },
1675 { "a1", true, true },
1676 kEmptyMatch },
1677 std::string() },
1679 // Ensure that all suggestions are considered, regardless of order.
1680 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
1681 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1682 { { "a", true, true },
1683 { "k a", false, true },
1684 { "h", true, false },
1685 { "g", true, false },
1686 { "f", true, false } },
1687 std::string() },
1688 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
1689 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
1690 "\"http://h.com\"],[],[],"
1691 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
1692 "\"NAVIGATION\", \"NAVIGATION\","
1693 "\"NAVIGATION\", \"NAVIGATION\","
1694 "\"NAVIGATION\"],"
1695 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
1696 { { "a", true, true },
1697 { "k a", false, true },
1698 { "h.com", false, false },
1699 { "g.com", false, false },
1700 { "f.com", false, false } },
1701 std::string() },
1703 // Ensure that incorrectly sized suggestion relevance lists are ignored.
1704 // Note that keyword suggestions by default (not in suggested relevance
1705 // mode) score more highly than the default verbatim.
1706 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
1707 { { "a", true, true },
1708 { "a1", true, true },
1709 { "a2", true, true },
1710 { "k a", false, true },
1711 kEmptyMatch },
1712 std::string() },
1713 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
1714 { { "a", true, true },
1715 { "a1", true, true },
1716 { "k a", false, true },
1717 kEmptyMatch, kEmptyMatch},
1718 std::string() },
1719 // In this case, ignoring the suggested relevance scores means we keep
1720 // only one navsuggest result.
1721 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1722 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1723 "\"google:suggestrelevance\":[1]}]",
1724 { { "a", true, true },
1725 { "a1.com", false, true },
1726 { "k a", false, true },
1727 kEmptyMatch, kEmptyMatch},
1728 std::string() },
1729 { "[\"a\",[\"http://a1.com\"],[],[],"
1730 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1731 "\"google:suggestrelevance\":[9999, 1]}]",
1732 { { "a", true, true },
1733 { "a1.com", false, true },
1734 { "k a", false, true },
1735 kEmptyMatch, kEmptyMatch},
1736 std::string() },
1738 // Ensure that all 'verbatim' results are merged with their maximum score.
1739 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1740 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1741 { { "a2", true, true },
1742 { "a", true, true },
1743 { "a1", true, true },
1744 { "k a", false, true },
1745 kEmptyMatch },
1746 "2" },
1747 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
1748 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
1749 "\"google:verbatimrelevance\":0}]",
1750 { { "a2", true, true },
1751 { "a", true, true },
1752 { "a1", true, true },
1753 { "k a", false, true },
1754 kEmptyMatch },
1755 "2" },
1757 // Ensure that verbatim is always generated without other suggestions.
1758 // TODO(mpearson): Ensure the value of verbatimrelevance is respected
1759 // (except when suggested relevances are ignored).
1760 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
1761 { { "a", true, true },
1762 { "k a", false, true },
1763 kEmptyMatch, kEmptyMatch, kEmptyMatch},
1764 std::string() },
1765 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
1766 { { "a", true, true },
1767 { "k a", false, true },
1768 kEmptyMatch, kEmptyMatch, kEmptyMatch},
1769 std::string() },
1771 // Check that navsuggestions will be demoted below queries.
1772 // (Navsuggestions are not allowed to appear first.) In the process,
1773 // make sure the navsuggestions still remain in the same order.
1774 // First, check the situation where navsuggest scores more than verbatim
1775 // and there are no query suggestions.
1776 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1777 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1778 "\"google:verbatimrelevance\":9990,"
1779 "\"google:suggestrelevance\":[9998, 9999]}]",
1780 { { "a", true, true },
1781 { "a2.com", false, true },
1782 { "a1.com", false, true },
1783 { "k a", false, true },
1784 kEmptyMatch },
1785 std::string() },
1786 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1787 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1788 "\"google:verbatimrelevance\":9990,"
1789 "\"google:suggestrelevance\":[9999, 9998]}]",
1790 { { "a", true, true },
1791 { "a1.com", false, true },
1792 { "a2.com", false, true },
1793 { "k a", false, true },
1794 kEmptyMatch },
1795 std::string() },
1796 { "[\"a\",[\"https://a/\"],[],[],"
1797 "{\"google:suggesttype\":[\"NAVIGATION\"],"
1798 "\"google:suggestrelevance\":[9999]}]",
1799 { { "a", true, true },
1800 { "https://a", false, true },
1801 { "k a", false, true },
1802 kEmptyMatch,
1803 kEmptyMatch },
1804 std::string() },
1805 // Check when navsuggest scores more than verbatim and there is query
1806 // suggestion but it scores lower.
1807 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1808 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1809 "\"google:verbatimrelevance\":9990,"
1810 "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
1811 { { "a", true, true },
1812 { "a2.com", false, true },
1813 { "a1.com", false, true },
1814 { "a3", true, true },
1815 { "k a", false, true } },
1816 std::string() },
1817 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1818 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1819 "\"google:verbatimrelevance\":9990,"
1820 "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
1821 { { "a", true, true },
1822 { "a1.com", false, true },
1823 { "a2.com", false, true },
1824 { "a3", true, true },
1825 { "k a", false, true } },
1826 std::string() },
1827 // Check when navsuggest scores more than a query suggestion. There is
1828 // a verbatim but it scores lower.
1829 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1830 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1831 "\"google:verbatimrelevance\":9990,"
1832 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1833 { { "a3", true, true },
1834 { "a2.com", false, true },
1835 { "a1.com", false, true },
1836 { "a", true, true },
1837 { "k a", false, true } },
1838 "3" },
1839 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1840 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1841 "\"google:verbatimrelevance\":9990,"
1842 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1843 { { "a3", true, true },
1844 { "a1.com", false, true },
1845 { "a2.com", false, true },
1846 { "a", true, true },
1847 { "k a", false, true } },
1848 "3" },
1849 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1850 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1851 "\"google:verbatimrelevance\":0,"
1852 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
1853 { { "a3", true, true },
1854 { "a2.com", false, true },
1855 { "a1.com", false, true },
1856 { "k a", false, true },
1857 kEmptyMatch },
1858 "3" },
1859 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1860 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1861 "\"google:verbatimrelevance\":0,"
1862 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
1863 { { "a3", true, true },
1864 { "a1.com", false, true },
1865 { "a2.com", false, true },
1866 { "k a", false, true },
1867 kEmptyMatch },
1868 "3" },
1869 // Check when there is neither verbatim nor a query suggestion that,
1870 // because we can't demote navsuggestions below a query suggestion,
1871 // we abandon suggested relevance scores entirely. One consequence is
1872 // that this means we restore the keyword verbatim match. Note
1873 // that in this case of abandoning suggested relevance scores, we still
1874 // keep the navsuggestions in the same order, but we revert to only allowing
1875 // one navigation to appear because the scores are completely local.
1876 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1877 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1878 "\"google:verbatimrelevance\":0,"
1879 "\"google:suggestrelevance\":[9998, 9999]}]",
1880 { { "a", true, true },
1881 { "a2.com", false, true },
1882 { "k a", false, true },
1883 kEmptyMatch, kEmptyMatch},
1884 std::string() },
1885 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
1886 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
1887 "\"google:verbatimrelevance\":0,"
1888 "\"google:suggestrelevance\":[9999, 9998]}]",
1889 { { "a", true, true },
1890 { "a1.com", false, true },
1891 { "k a", false, true },
1892 kEmptyMatch, kEmptyMatch},
1893 std::string() },
1894 // More checks that everything works when it's not necessary to demote.
1895 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1896 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1897 "\"google:verbatimrelevance\":9990,"
1898 "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
1899 { { "a3", true, true },
1900 { "a2.com", false, true },
1901 { "a1.com", false, true },
1902 { "a", true, true },
1903 { "k a", false, true } },
1904 "3" },
1905 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
1906 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
1907 "\"google:verbatimrelevance\":9990,"
1908 "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
1909 { { "a3", true, true },
1910 { "a1.com", false, true },
1911 { "a2.com", false, true },
1912 { "a", true, true },
1913 { "k a", false, true } },
1914 "3" },
1917 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
1918 QueryForInput(ASCIIToUTF16("k a"), false, true);
1920 // Set up a default fetcher with no results.
1921 net::TestURLFetcher* default_fetcher =
1922 test_factory_.GetFetcherByID(
1923 SearchProvider::kDefaultProviderURLFetcherID);
1924 ASSERT_TRUE(default_fetcher);
1925 default_fetcher->set_response_code(200);
1926 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
1927 default_fetcher = NULL;
1929 // Set up a keyword fetcher with provided results.
1930 net::TestURLFetcher* keyword_fetcher =
1931 test_factory_.GetFetcherByID(
1932 SearchProvider::kKeywordProviderURLFetcherID);
1933 ASSERT_TRUE(keyword_fetcher);
1934 keyword_fetcher->set_response_code(200);
1935 keyword_fetcher->SetResponseString(cases[i].json);
1936 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
1937 keyword_fetcher = NULL;
1938 RunTillProviderDone();
1940 const std::string description = "for input with json=" + cases[i].json;
1941 const ACMatches& matches = provider_->matches();
1942 // The top match must inline and score as highly as calculated verbatim.
1943 ASSERT_FALSE(matches.empty());
1944 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
1945 matches[0].inline_autocompletion) << description;
1946 EXPECT_GE(matches[0].relevance, 1300) << description;
1948 size_t j = 0;
1949 // Ensure that the returned matches equal the expectations.
1950 for (; j < matches.size(); ++j) {
1951 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
1952 matches[j].contents) << description;
1953 EXPECT_EQ(cases[i].matches[j].from_keyword,
1954 matches[j].keyword == ASCIIToUTF16("k")) << description;
1955 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
1956 matches[j].allowed_to_be_default_match) << description;
1958 // Ensure that no expected matches are missing.
1959 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
1960 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
1961 "Case # " << i << " " << description;
1965 // This test is like KeywordFetcherSuggestRelevance above except it
1966 // enables the field trial that causes the omnibox to be willing to
1967 // reorder matches to guarantee the top result is a legal default
1968 // match. This field trial causes SearchProvider to allow some
1969 // constraints to be violated that it wouldn't normally because the
1970 // omnibox will fix the problems later.
1971 TEST_F(SearchProviderTest, KeywordFetcherSuggestRelevanceWithReorder) {
1972 struct KeywordFetcherMatch {
1973 std::string contents;
1974 bool from_keyword;
1975 bool allowed_to_be_default_match;
1977 const KeywordFetcherMatch kEmptyMatch = { kNotApplicable, false, false };
1978 struct {
1979 const std::string json;
1980 const KeywordFetcherMatch matches[5];
1981 const std::string inline_autocompletion;
1982 } cases[] = {
1983 // Ensure that suggest relevance scores reorder matches and that
1984 // the keyword verbatim (lacking a suggested verbatim score) beats
1985 // the default provider verbatim.
1986 { "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
1987 { { "a", true, true },
1988 { "k a", false, true },
1989 { "c", true, false },
1990 { "b", true, false },
1991 kEmptyMatch },
1992 std::string() },
1993 // Again, check that relevance scores reorder matches, just this
1994 // time with navigation matches. This also checks that with
1995 // suggested relevance scores we allow multiple navsuggest results.
1996 // It's odd that navsuggest results that come from a keyword
1997 // provider are marked as not a keyword result. I think this
1998 // comes from them not going to a keyword search engine.
1999 // TODO(mpearson): Investigate the implications (if any) of
2000 // tagging these results appropriately. If so, do it because it
2001 // makes more sense.
2002 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"d\"],[],[],"
2003 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2004 "\"google:suggestrelevance\":[1301, 1302, 1303]}]",
2005 { { "a", true, true },
2006 { "d", true, false },
2007 { "c.com", false, false },
2008 { "b.com", false, false },
2009 { "k a", false, true }, },
2010 std::string() },
2012 // Without suggested relevance scores, we should only allow one
2013 // navsuggest result to be be displayed.
2014 { "[\"a\",[\"http://b.com\", \"http://c.com\"],[],[],"
2015 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"]}]",
2016 { { "a", true, true },
2017 { "b.com", false, false },
2018 { "k a", false, true },
2019 kEmptyMatch, kEmptyMatch },
2020 std::string() },
2022 // Ensure that verbatimrelevance scores reorder or suppress verbatim.
2023 // Negative values will have no effect; the calculated value will be used.
2024 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9999,"
2025 "\"google:suggestrelevance\":[9998]}]",
2026 { { "a", true, true },
2027 { "a1", true, true },
2028 { "k a", false, true },
2029 kEmptyMatch, kEmptyMatch },
2030 std::string() },
2031 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":9998,"
2032 "\"google:suggestrelevance\":[9999]}]",
2033 { { "a1", true, true },
2034 { "a", true, true },
2035 { "k a", false, true },
2036 kEmptyMatch, kEmptyMatch },
2037 "1" },
2038 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0,"
2039 "\"google:suggestrelevance\":[9999]}]",
2040 { { "a1", true, true },
2041 { "k a", false, true },
2042 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2043 "1" },
2044 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":-1,"
2045 "\"google:suggestrelevance\":[9999]}]",
2046 { { "a1", true, true },
2047 { "a", true, true },
2048 { "k a", false, true },
2049 kEmptyMatch, kEmptyMatch },
2050 "1" },
2051 { "[\"a\",[\"http://a.com\"],[],[],"
2052 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2053 "\"google:verbatimrelevance\":9999,"
2054 "\"google:suggestrelevance\":[9998]}]",
2055 { { "a", true, true },
2056 { "a.com", false, true },
2057 { "k a", false, true },
2058 kEmptyMatch, kEmptyMatch },
2059 std::string() },
2061 // Ensure that both types of relevance scores reorder matches together.
2062 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[9999, 9997],"
2063 "\"google:verbatimrelevance\":9998}]",
2064 { { "a1", true, true },
2065 { "a", true, true },
2066 { "a2", true, true },
2067 { "k a", false, true },
2068 kEmptyMatch },
2069 "1" },
2071 // Check that non-inlinable matches may be ranked as the highest result
2072 // if there is at least one inlineable match.
2073 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999]}]",
2074 { { "b", true, false },
2075 { "a", true, true },
2076 { "k a", false, true },
2077 kEmptyMatch, kEmptyMatch },
2078 std::string() },
2079 { "[\"a\",[\"b\"],[],[],{\"google:suggestrelevance\":[9999],"
2080 "\"google:verbatimrelevance\":0}]",
2081 { { "b", true, false },
2082 { "k a", false, true },
2083 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2084 std::string() },
2085 { "[\"a\",[\"http://b.com\"],[],[],"
2086 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2087 "\"google:suggestrelevance\":[9999]}]",
2088 { { "b.com", false, false },
2089 { "a", true, true },
2090 { "k a", false, true },
2091 kEmptyMatch, kEmptyMatch },
2092 std::string() },
2093 { "[\"a\",[\"http://b.com\"],[],[],"
2094 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2095 "\"google:suggestrelevance\":[9999],"
2096 "\"google:verbatimrelevance\":0}]",
2097 { { "b.com", false, false },
2098 { "k a", false, true },
2099 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2100 std::string() },
2102 // The top result does not have to score as highly as calculated
2103 // verbatim. i.e., there are no minimum score restrictions in
2104 // this provider.
2105 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":0}]",
2106 { { "a1", true, true },
2107 { "k a", false, true },
2108 kEmptyMatch, kEmptyMatch, kEmptyMatch },
2109 "1" },
2110 { "[\"a\",[\"a1\"],[],[],{\"google:verbatimrelevance\":1}]",
2111 { { "a1", true, true },
2112 { "k a", false, true },
2113 { "a", true, true },
2114 kEmptyMatch, kEmptyMatch},
2115 "1" },
2116 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[1],"
2117 "\"google:verbatimrelevance\":0}]",
2118 { { "k a", false, true },
2119 { "a1", true, true },
2120 kEmptyMatch, kEmptyMatch, kEmptyMatch},
2121 std::string() },
2122 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 2],"
2123 "\"google:verbatimrelevance\":0}]",
2125 { "k a", false, true },
2126 { "a2", true, true },
2127 { "a1", true, true },
2128 kEmptyMatch, kEmptyMatch },
2129 std::string() },
2130 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1, 3],"
2131 "\"google:verbatimrelevance\":2}]",
2132 { { "k a", false, true },
2133 { "a2", true, true },
2134 { "a", true, true },
2135 { "a1", true, true },
2136 kEmptyMatch },
2137 std::string() },
2139 // Ensure that all suggestions are considered, regardless of order.
2140 { "[\"a\",[\"b\", \"c\", \"d\", \"e\", \"f\", \"g\", \"h\"],[],[],"
2141 "{\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
2142 { { "a", true, true },
2143 { "k a", false, true },
2144 { "h", true, false },
2145 { "g", true, false },
2146 { "f", true, false } },
2147 std::string() },
2148 { "[\"a\",[\"http://b.com\", \"http://c.com\", \"http://d.com\","
2149 "\"http://e.com\", \"http://f.com\", \"http://g.com\","
2150 "\"http://h.com\"],[],[],"
2151 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\","
2152 "\"NAVIGATION\", \"NAVIGATION\","
2153 "\"NAVIGATION\", \"NAVIGATION\","
2154 "\"NAVIGATION\"],"
2155 "\"google:suggestrelevance\":[1, 2, 3, 4, 5, 6, 7]}]",
2156 { { "a", true, true },
2157 { "k a", false, true },
2158 { "h.com", false, false },
2159 { "g.com", false, false },
2160 { "f.com", false, false } },
2161 std::string() },
2163 // Ensure that incorrectly sized suggestion relevance lists are ignored.
2164 // Note that keyword suggestions by default (not in suggested relevance
2165 // mode) score more highly than the default verbatim.
2166 { "[\"a\",[\"a1\", \"a2\"],[],[],{\"google:suggestrelevance\":[1]}]",
2167 { { "a", true, true },
2168 { "a1", true, true },
2169 { "a2", true, true },
2170 { "k a", false, true },
2171 kEmptyMatch },
2172 std::string() },
2173 { "[\"a\",[\"a1\"],[],[],{\"google:suggestrelevance\":[9999, 1]}]",
2174 { { "a", true, true },
2175 { "a1", true, true },
2176 { "k a", false, true },
2177 kEmptyMatch, kEmptyMatch},
2178 std::string() },
2179 // In this case, ignoring the suggested relevance scores means we keep
2180 // only one navsuggest result.
2181 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2182 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2183 "\"google:suggestrelevance\":[1]}]",
2184 { { "a", true, true },
2185 { "a1.com", false, true },
2186 { "k a", false, true },
2187 kEmptyMatch, kEmptyMatch},
2188 std::string() },
2189 { "[\"a\",[\"http://a1.com\"],[],[],"
2190 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2191 "\"google:suggestrelevance\":[9999, 1]}]",
2192 { { "a", true, true },
2193 { "a1.com", false, true },
2194 { "k a", false, true },
2195 kEmptyMatch, kEmptyMatch},
2196 std::string() },
2198 // Ensure that all 'verbatim' results are merged with their maximum score.
2199 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
2200 "{\"google:suggestrelevance\":[9998, 9997, 9999]}]",
2201 { { "a2", true, true },
2202 { "a", true, true },
2203 { "a1", true, true },
2204 { "k a", false, true },
2205 kEmptyMatch },
2206 "2" },
2207 { "[\"a\",[\"a\", \"a1\", \"a2\"],[],[],"
2208 "{\"google:suggestrelevance\":[9998, 9997, 9999],"
2209 "\"google:verbatimrelevance\":0}]",
2210 { { "a2", true, true },
2211 { "a", true, true },
2212 { "a1", true, true },
2213 { "k a", false, true },
2214 kEmptyMatch },
2215 "2" },
2217 // Ensure that verbatim is always generated without other suggestions.
2218 // TODO(mpearson): Ensure the value of verbatimrelevance is respected
2219 // (except when suggested relevances are ignored).
2220 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":1}]",
2221 { { "k a", false, true },
2222 { "a", true, true },
2223 kEmptyMatch, kEmptyMatch, kEmptyMatch},
2224 std::string() },
2225 { "[\"a\",[],[],[],{\"google:verbatimrelevance\":0}]",
2226 { { "a", true, true },
2227 { "k a", false, true },
2228 kEmptyMatch, kEmptyMatch, kEmptyMatch},
2229 std::string() },
2231 // Check that navsuggestions will be demoted below queries.
2232 // (Navsuggestions are not allowed to appear first.) In the process,
2233 // make sure the navsuggestions still remain in the same order.
2234 // First, check the situation where navsuggest scores more than verbatim
2235 // and there are no query suggestions.
2236 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2237 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2238 "\"google:verbatimrelevance\":9990,"
2239 "\"google:suggestrelevance\":[9998, 9999]}]",
2240 { { "a", true, true },
2241 { "a2.com", false, true },
2242 { "a1.com", false, true },
2243 { "k a", false, true },
2244 kEmptyMatch },
2245 std::string() },
2246 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2247 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2248 "\"google:verbatimrelevance\":9990,"
2249 "\"google:suggestrelevance\":[9999, 9998]}]",
2250 { { "a", true, true },
2251 { "a1.com", false, true },
2252 { "a2.com", false, true },
2253 { "k a", false, true },
2254 kEmptyMatch },
2255 std::string() },
2256 { "[\"a\",[\"https://a/\"],[],[],"
2257 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2258 "\"google:suggestrelevance\":[9999]}]",
2259 { { "a", true, true },
2260 { "https://a", false, true },
2261 { "k a", false, true },
2262 kEmptyMatch,
2263 kEmptyMatch },
2264 std::string() },
2265 // Check when navsuggest scores more than verbatim and there is query
2266 // suggestion but it scores lower.
2267 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2268 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2269 "\"google:verbatimrelevance\":9990,"
2270 "\"google:suggestrelevance\":[9998, 9999, 1300]}]",
2271 { { "a", true, true },
2272 { "a2.com", false, true },
2273 { "a1.com", false, true },
2274 { "a3", true, true },
2275 { "k a", false, true } },
2276 std::string() },
2277 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2278 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2279 "\"google:verbatimrelevance\":9990,"
2280 "\"google:suggestrelevance\":[9999, 9998, 1300]}]",
2281 { { "a", true, true },
2282 { "a1.com", false, true },
2283 { "a2.com", false, true },
2284 { "a3", true, true },
2285 { "k a", false, true } },
2286 std::string() },
2287 // Check when navsuggest scores more than a query suggestion. There is
2288 // a verbatim but it scores lower.
2289 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2290 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2291 "\"google:verbatimrelevance\":9990,"
2292 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2293 { { "a3", true, true },
2294 { "a2.com", false, true },
2295 { "a1.com", false, true },
2296 { "a", true, true },
2297 { "k a", false, true } },
2298 "3" },
2299 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2300 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2301 "\"google:verbatimrelevance\":9990,"
2302 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2303 { { "a3", true, true },
2304 { "a1.com", false, true },
2305 { "a2.com", false, true },
2306 { "a", true, true },
2307 { "k a", false, true } },
2308 "3" },
2309 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2310 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2311 "\"google:verbatimrelevance\":0,"
2312 "\"google:suggestrelevance\":[9998, 9999, 9997]}]",
2313 { { "a3", true, true },
2314 { "a2.com", false, true },
2315 { "a1.com", false, true },
2316 { "k a", false, true },
2317 kEmptyMatch },
2318 "3" },
2319 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2320 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2321 "\"google:verbatimrelevance\":0,"
2322 "\"google:suggestrelevance\":[9999, 9998, 9997]}]",
2323 { { "a3", true, true },
2324 { "a1.com", false, true },
2325 { "a2.com", false, true },
2326 { "k a", false, true },
2327 kEmptyMatch },
2328 "3" },
2329 // Check when there is neither verbatim nor a query suggestion that,
2330 // because we can't demote navsuggestions below a query suggestion,
2331 // we abandon suggested relevance scores entirely. One consequence is
2332 // that this means we restore the keyword verbatim match. Note
2333 // that in this case of abandoning suggested relevance scores, we still
2334 // keep the navsuggestions in the same order, but we revert to only allowing
2335 // one navigation to appear because the scores are completely local.
2336 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2337 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2338 "\"google:verbatimrelevance\":0,"
2339 "\"google:suggestrelevance\":[9998, 9999]}]",
2340 { { "a", true, true },
2341 { "a2.com", false, true },
2342 { "k a", false, true },
2343 kEmptyMatch, kEmptyMatch},
2344 std::string() },
2345 { "[\"a\",[\"http://a1.com\", \"http://a2.com\"],[],[],"
2346 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\"],"
2347 "\"google:verbatimrelevance\":0,"
2348 "\"google:suggestrelevance\":[9999, 9998]}]",
2349 { { "a", true, true },
2350 { "a1.com", false, true },
2351 { "k a", false, true },
2352 kEmptyMatch, kEmptyMatch},
2353 std::string() },
2354 // More checks that everything works when it's not necessary to demote.
2355 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2356 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2357 "\"google:verbatimrelevance\":9990,"
2358 "\"google:suggestrelevance\":[9997, 9998, 9999]}]",
2359 { { "a3", true, true },
2360 { "a2.com", false, true },
2361 { "a1.com", false, true },
2362 { "a", true, true },
2363 { "k a", false, true } },
2364 "3" },
2365 { "[\"a\",[\"http://a1.com\", \"http://a2.com\", \"a3\"],[],[],"
2366 "{\"google:suggesttype\":[\"NAVIGATION\", \"NAVIGATION\", \"QUERY\"],"
2367 "\"google:verbatimrelevance\":9990,"
2368 "\"google:suggestrelevance\":[9998, 9997, 9999]}]",
2369 { { "a3", true, true },
2370 { "a1.com", false, true },
2371 { "a2.com", false, true },
2372 { "a", true, true },
2373 { "k a", false, true } },
2374 "3" },
2377 std::map<std::string, std::string> params;
2378 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
2379 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled;
2380 ASSERT_TRUE(chrome_variations::AssociateVariationParams(
2381 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
2382 base::FieldTrialList::CreateFieldTrial(
2383 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
2385 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2386 QueryForInput(ASCIIToUTF16("k a"), false, true);
2388 // Set up a default fetcher with no results.
2389 net::TestURLFetcher* default_fetcher =
2390 test_factory_.GetFetcherByID(
2391 SearchProvider::kDefaultProviderURLFetcherID);
2392 ASSERT_TRUE(default_fetcher);
2393 default_fetcher->set_response_code(200);
2394 default_fetcher->delegate()->OnURLFetchComplete(default_fetcher);
2395 default_fetcher = NULL;
2397 // Set up a keyword fetcher with provided results.
2398 net::TestURLFetcher* keyword_fetcher =
2399 test_factory_.GetFetcherByID(
2400 SearchProvider::kKeywordProviderURLFetcherID);
2401 ASSERT_TRUE(keyword_fetcher);
2402 keyword_fetcher->set_response_code(200);
2403 keyword_fetcher->SetResponseString(cases[i].json);
2404 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
2405 keyword_fetcher = NULL;
2406 RunTillProviderDone();
2408 const std::string description = "for input with json=" + cases[i].json;
2409 const ACMatches& matches = provider_->matches();
2410 // The top match must inline and score as highly as calculated verbatim.
2411 ASSERT_FALSE(matches.empty());
2412 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2413 matches[0].inline_autocompletion) << description;
2415 size_t j = 0;
2416 // Ensure that the returned matches equal the expectations.
2417 for (; j < matches.size(); ++j) {
2418 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j].contents),
2419 matches[j].contents) << description;
2420 EXPECT_EQ(cases[i].matches[j].from_keyword,
2421 matches[j].keyword == ASCIIToUTF16("k")) << description;
2422 EXPECT_EQ(cases[i].matches[j].allowed_to_be_default_match,
2423 matches[j].allowed_to_be_default_match) << description;
2425 // Ensure that no expected matches are missing.
2426 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2427 EXPECT_EQ(kNotApplicable, cases[i].matches[j].contents) <<
2428 "Case # " << i << " " << description;
2432 TEST_F(SearchProviderTest, LocalAndRemoteRelevances) {
2433 // Enable Instant Extended in order to allow an increased number of
2434 // suggestions.
2435 chrome::EnableInstantExtendedAPIForTesting();
2437 // We hardcode the string "term1" below, so ensure that the search term that
2438 // got added to history already is that string.
2439 ASSERT_EQ(ASCIIToUTF16("term1"), term1_);
2440 string16 term = term1_.substr(0, term1_.length() - 1);
2442 AddSearchToHistory(default_t_url_, term + ASCIIToUTF16("2"), 2);
2443 profile_.BlockUntilHistoryProcessesPendingRequests();
2445 struct {
2446 const string16 input;
2447 const std::string json;
2448 const std::string matches[6];
2449 } cases[] = {
2450 // The history results outscore the default verbatim score. term2 has more
2451 // visits so it outscores term1. The suggestions are still returned since
2452 // they're server-scored.
2453 { term,
2454 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2455 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2456 "\"google:suggestrelevance\":[1, 2, 3]}]",
2457 { "term2", "term1", "term", "a3", "a2", "a1" } },
2458 // Because we already have three suggestions by the time we see the history
2459 // results, they don't get returned.
2460 { term,
2461 "[\"term\",[\"a1\", \"a2\", \"a3\"],[],[],"
2462 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\"],"
2463 "\"google:verbatimrelevance\":1450,"
2464 "\"google:suggestrelevance\":[1440, 1430, 1420]}]",
2465 { "term", "a1", "a2", "a3", kNotApplicable, kNotApplicable } },
2466 // If we only have two suggestions, we have room for a history result.
2467 { term,
2468 "[\"term\",[\"a1\", \"a2\"],[],[],"
2469 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
2470 "\"google:verbatimrelevance\":1450,"
2471 "\"google:suggestrelevance\":[1430, 1410]}]",
2472 { "term", "a1", "a2", "term2", kNotApplicable, kNotApplicable } },
2473 // If we have more than three suggestions, they should all be returned as
2474 // long as we have enough total space for them.
2475 { term,
2476 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2477 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2478 "\"google:verbatimrelevance\":1450,"
2479 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410]}]",
2480 { "term", "a1", "a2", "a3", "a4", kNotApplicable } },
2481 { term,
2482 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\", \"a5\", \"a6\"],[],[],"
2483 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\","
2484 "\"QUERY\", \"QUERY\"],"
2485 "\"google:verbatimrelevance\":1450,"
2486 "\"google:suggestrelevance\":[1440, 1430, 1420, 1410, 1400, 1390]}]",
2487 { "term", "a1", "a2", "a3", "a4", "a5" } },
2488 { term,
2489 "[\"term\",[\"a1\", \"a2\", \"a3\", \"a4\"],[],[],"
2490 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"QUERY\", \"QUERY\"],"
2491 "\"google:verbatimrelevance\":1450,"
2492 "\"google:suggestrelevance\":[1430, 1410, 1390, 1370]}]",
2493 { "term", "a1", "a2", "term2", "a3", "a4" } },
2494 // When the input looks like a URL, we disallow having a query as the
2495 // highest-ranking result. If the query was provided by a suggestion, we
2496 // reset the suggest scores to enforce this (see
2497 // SearchProvider::UpdateMatches()). Even if we reset the suggest scores,
2498 // however, we should still allow navsuggestions to be treated as
2499 // server-provided.
2500 { ASCIIToUTF16("a.com"),
2501 "[\"a.com\",[\"a1\", \"a2\", \"a.com/1\", \"a.com/2\"],[],[],"
2502 "{\"google:suggesttype\":[\"QUERY\", \"QUERY\", \"NAVIGATION\","
2503 "\"NAVIGATION\"],"
2504 // A verbatim query for URL-like input scores 850, so the navigation
2505 // scores here should bracket it.
2506 "\"google:suggestrelevance\":[9999, 9998, 900, 800]}]",
2507 { "a.com/1", "a.com", "a.com/2", "a1", kNotApplicable, kNotApplicable } },
2510 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2511 QueryForInput(cases[i].input, false, false);
2512 net::TestURLFetcher* fetcher =
2513 test_factory_.GetFetcherByID(
2514 SearchProvider::kDefaultProviderURLFetcherID);
2515 ASSERT_TRUE(fetcher);
2516 fetcher->set_response_code(200);
2517 fetcher->SetResponseString(cases[i].json);
2518 fetcher->delegate()->OnURLFetchComplete(fetcher);
2519 RunTillProviderDone();
2521 const std::string description = "for input with json=" + cases[i].json;
2522 const ACMatches& matches = provider_->matches();
2524 // Ensure no extra matches are present.
2525 ASSERT_LE(matches.size(), 6U);
2527 size_t j = 0;
2528 // Ensure that the returned matches equal the expectations.
2529 for (; j < matches.size(); ++j)
2530 EXPECT_EQ(ASCIIToUTF16(cases[i].matches[j]),
2531 matches[j].contents) << description;
2532 // Ensure that no expected matches are missing.
2533 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j)
2534 EXPECT_EQ(kNotApplicable, cases[i].matches[j]) <<
2535 "Case # " << i << " " << description;
2539 // Verifies suggest relevance behavior for URL input.
2540 TEST_F(SearchProviderTest, DefaultProviderSuggestRelevanceScoringUrlInput) {
2541 struct DefaultFetcherUrlInputMatch {
2542 const std::string match_contents;
2543 AutocompleteMatch::Type match_type;
2544 bool allowed_to_be_default_match;
2546 const DefaultFetcherUrlInputMatch kEmptyMatch =
2547 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2548 struct {
2549 const std::string input;
2550 const std::string json;
2551 const DefaultFetcherUrlInputMatch output[4];
2552 } cases[] = {
2553 // Ensure topmost NAVIGATION matches are allowed for URL input.
2554 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2555 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2556 "\"google:suggestrelevance\":[9999]}]",
2557 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true },
2558 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2559 kEmptyMatch, kEmptyMatch } },
2560 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2561 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2562 "\"google:suggestrelevance\":[9999]}]",
2563 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true },
2564 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2565 kEmptyMatch, kEmptyMatch } },
2567 // Ensure topmost SUGGEST matches are not allowed for URL input.
2568 // SearchProvider disregards search and verbatim suggested relevances.
2569 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2570 "{\"google:suggestrelevance\":[9999]}]",
2571 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2572 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2573 kEmptyMatch, kEmptyMatch } },
2574 { "a.com", "[\"a.com\",[\"a.com/a\"],[],[],"
2575 "{\"google:suggestrelevance\":[9999]}]",
2576 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2577 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2578 kEmptyMatch, kEmptyMatch } },
2580 // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
2581 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
2582 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2583 "\"google:suggestrelevance\":[9999, 9998]}]",
2584 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2585 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2586 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2587 kEmptyMatch } },
2588 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://a.com/b\"],[],[],"
2589 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2590 "\"google:suggestrelevance\":[9998, 9997],"
2591 "\"google:verbatimrelevance\":9999}]",
2592 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2593 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2594 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2595 kEmptyMatch } },
2597 // Ensure the fallback mechanism disallows non-inlinable NAVIGATION matches.
2598 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2599 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2600 "\"google:suggestrelevance\":[9999, 9998]}]",
2601 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2602 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false },
2603 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2604 kEmptyMatch } },
2605 { "a.com", "[\"a.com\",[\"a.com/a\", \"http://abc.com\"],[],[],"
2606 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2607 "\"google:suggestrelevance\":[9998, 9997],"
2608 "\"google:verbatimrelevance\":9999}]",
2609 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2610 { "abc.com", AutocompleteMatchType::NAVSUGGEST, false },
2611 { "a.com/a", AutocompleteMatchType::SEARCH_SUGGEST, true },
2612 kEmptyMatch } },
2615 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2616 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2617 net::TestURLFetcher* fetcher =
2618 test_factory_.GetFetcherByID(
2619 SearchProvider::kDefaultProviderURLFetcherID);
2620 ASSERT_TRUE(fetcher);
2621 fetcher->set_response_code(200);
2622 fetcher->SetResponseString(cases[i].json);
2623 fetcher->delegate()->OnURLFetchComplete(fetcher);
2624 RunTillProviderDone();
2626 size_t j = 0;
2627 const ACMatches& matches = provider_->matches();
2628 // Ensure that the returned matches equal the expectations.
2629 for (; j < matches.size(); ++j) {
2630 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2631 matches[j].contents);
2632 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2633 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2634 matches[j].allowed_to_be_default_match);
2636 // Ensure that no expected matches are missing.
2637 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2638 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2639 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2640 cases[i].output[j].match_type);
2641 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2646 // This test is like DefaultProviderSuggestRelevanceScoringUrlInput
2647 // above except it enables the field trial that causes the omnibox to
2648 // be willing to reorder matches to guarantee the top result is a
2649 // legal default match. This field trial causes SearchProvider to
2650 // allow some constraints to be violated that it wouldn't normally
2651 // because the omnibox will fix the problems later.
2652 TEST_F(SearchProviderTest,
2653 DefaultProviderSuggestRelevanceScoringUrlInputWithReorder) {
2654 struct DefaultFetcherUrlInputMatch {
2655 const std::string match_contents;
2656 AutocompleteMatch::Type match_type;
2657 bool allowed_to_be_default_match;
2659 const DefaultFetcherUrlInputMatch kEmptyMatch =
2660 { kNotApplicable, AutocompleteMatchType::NUM_TYPES, false };
2661 struct {
2662 const std::string input;
2663 const std::string json;
2664 const DefaultFetcherUrlInputMatch output[4];
2665 } cases[] = {
2666 // Ensure NAVIGATION matches are allowed to be listed first for URL
2667 // input regardless of whether the match is inlineable. Note that
2668 // non-inlineable matches should not be allowed to be the default match.
2669 { "a.com", "[\"a.com\",[\"http://b.com/\"],[],[],"
2670 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2671 "\"google:suggestrelevance\":[9999]}]",
2672 { { "b.com", AutocompleteMatchType::NAVSUGGEST, false },
2673 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2674 kEmptyMatch, kEmptyMatch } },
2675 { "a.com", "[\"a.com\",[\"https://b.com\"],[],[],"
2676 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2677 "\"google:suggestrelevance\":[9999]}]",
2678 { { "https://b.com", AutocompleteMatchType::NAVSUGGEST, false },
2679 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2680 kEmptyMatch, kEmptyMatch } },
2681 { "a.com", "[\"a.com\",[\"http://a.com/a\"],[],[],"
2682 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2683 "\"google:suggestrelevance\":[9999]}]",
2684 { { "a.com/a", AutocompleteMatchType::NAVSUGGEST, true },
2685 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2686 kEmptyMatch, kEmptyMatch } },
2687 { "a.com", "[\"a.com\",[\"https://a.com\"],[],[],"
2688 "{\"google:suggesttype\":[\"NAVIGATION\"],"
2689 "\"google:suggestrelevance\":[9999]}]",
2690 { { "https://a.com", AutocompleteMatchType::NAVSUGGEST, true },
2691 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2692 kEmptyMatch, kEmptyMatch } },
2694 // Ensure topmost inlineable SUGGEST matches are NOT allowed for URL
2695 // input. SearchProvider disregards search and verbatim suggested
2696 // relevances.
2697 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2698 "{\"google:suggestrelevance\":[9999]}]",
2699 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2700 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2701 kEmptyMatch, kEmptyMatch } },
2702 { "a.com", "[\"a.com\",[\"a.com info\"],[],[],"
2703 "{\"google:suggestrelevance\":[9999]}]",
2704 { { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2705 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2706 kEmptyMatch, kEmptyMatch } },
2708 // Ensure the fallback mechanism allows inlinable NAVIGATION matches.
2709 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2710 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2711 "\"google:suggestrelevance\":[9999, 9998]}]",
2712 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2713 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2714 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2715 kEmptyMatch } },
2716 { "a.com", "[\"a.com\",[\"a.com info\", \"http://a.com/b\"],[],[],"
2717 "{\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
2718 "\"google:suggestrelevance\":[9998, 9997],"
2719 "\"google:verbatimrelevance\":9999}]",
2720 { { "a.com/b", AutocompleteMatchType::NAVSUGGEST, true },
2721 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2722 { "a.com info", AutocompleteMatchType::SEARCH_SUGGEST, true },
2723 kEmptyMatch } },
2725 // Ensure topmost non-inlineable SUGGEST matches are allowed for URL
2726 // input assuming the top inlineable match is not a query (i.e., is a
2727 // NAVSUGGEST).
2728 { "a.com", "[\"a.com\",[\"info\"],[],[],"
2729 "{\"google:suggestrelevance\":[9999]}]",
2730 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2731 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2732 kEmptyMatch, kEmptyMatch } },
2733 { "a.com", "[\"a.com\",[\"info\"],[],[],"
2734 "{\"google:suggestrelevance\":[9999]}]",
2735 { { "info", AutocompleteMatchType::SEARCH_SUGGEST, false },
2736 { "a.com", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, true },
2737 kEmptyMatch, kEmptyMatch } },
2740 std::map<std::string, std::string> params;
2741 params[std::string(OmniboxFieldTrial::kReorderForLegalDefaultMatchRule) +
2742 ":*:*"] = OmniboxFieldTrial::kReorderForLegalDefaultMatchRuleEnabled;
2743 ASSERT_TRUE(chrome_variations::AssociateVariationParams(
2744 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A", params));
2745 base::FieldTrialList::CreateFieldTrial(
2746 OmniboxFieldTrial::kBundledExperimentFieldTrialName, "A");
2748 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2749 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2750 net::TestURLFetcher* fetcher =
2751 test_factory_.GetFetcherByID(
2752 SearchProvider::kDefaultProviderURLFetcherID);
2753 ASSERT_TRUE(fetcher);
2754 fetcher->set_response_code(200);
2755 fetcher->SetResponseString(cases[i].json);
2756 fetcher->delegate()->OnURLFetchComplete(fetcher);
2757 RunTillProviderDone();
2759 size_t j = 0;
2760 const ACMatches& matches = provider_->matches();
2761 // Ensure that the returned matches equal the expectations.
2762 for (; j < matches.size(); ++j) {
2763 EXPECT_EQ(ASCIIToUTF16(cases[i].output[j].match_contents),
2764 matches[j].contents);
2765 EXPECT_EQ(cases[i].output[j].match_type, matches[j].type);
2766 EXPECT_EQ(cases[i].output[j].allowed_to_be_default_match,
2767 matches[j].allowed_to_be_default_match);
2769 // Ensure that no expected matches are missing.
2770 for (; j < ARRAYSIZE_UNSAFE(cases[i].output); ++j) {
2771 EXPECT_EQ(kNotApplicable, cases[i].output[j].match_contents);
2772 EXPECT_EQ(AutocompleteMatchType::NUM_TYPES,
2773 cases[i].output[j].match_type);
2774 EXPECT_FALSE(cases[i].output[j].allowed_to_be_default_match);
2779 // A basic test that verifies the field trial triggered parsing logic.
2780 TEST_F(SearchProviderTest, FieldTrialTriggeredParsing) {
2781 QueryForInput(ASCIIToUTF16("foo"), false, false);
2783 // Make sure the default providers suggest service was queried.
2784 net::TestURLFetcher* fetcher = test_factory_.GetFetcherByID(
2785 SearchProvider::kDefaultProviderURLFetcherID);
2786 ASSERT_TRUE(fetcher);
2788 // Tell the SearchProvider the suggest query is done.
2789 fetcher->set_response_code(200);
2790 fetcher->SetResponseString(
2791 "[\"foo\",[\"foo bar\"],[\"\"],[],"
2792 "{\"google:suggesttype\":[\"QUERY\"],"
2793 "\"google:fieldtrialtriggered\":true}]");
2794 fetcher->delegate()->OnURLFetchComplete(fetcher);
2795 fetcher = NULL;
2797 // Run till the history results complete.
2798 RunTillProviderDone();
2801 // Check for the match and field trial triggered bits.
2802 AutocompleteMatch match;
2803 EXPECT_TRUE(FindMatchWithContents(ASCIIToUTF16("foo bar"), &match));
2804 ProvidersInfo providers_info;
2805 provider_->AddProviderInfo(&providers_info);
2806 ASSERT_EQ(1U, providers_info.size());
2807 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2808 EXPECT_EQ(1, providers_info[0].field_trial_triggered_in_session_size());
2811 // Reset the session and check that bits are reset.
2812 provider_->ResetSession();
2813 ProvidersInfo providers_info;
2814 provider_->AddProviderInfo(&providers_info);
2815 ASSERT_EQ(1U, providers_info.size());
2816 EXPECT_EQ(1, providers_info[0].field_trial_triggered_size());
2817 EXPECT_EQ(0, providers_info[0].field_trial_triggered_in_session_size());
2821 // Verifies inline autocompletion of navigational results.
2822 TEST_F(SearchProviderTest, NavigationInline) {
2823 struct {
2824 const std::string input;
2825 const std::string url;
2826 // Test the expected fill_into_edit, which may drop "http://".
2827 // Some cases do not trim "http://" to match from the start of the scheme.
2828 const std::string fill_into_edit;
2829 const std::string inline_autocompletion;
2830 const bool allowed_to_be_default_match;
2831 } cases[] = {
2832 // Do not inline matches that do not contain the input; trim http as needed.
2833 { "x", "http://www.abc.com",
2834 "www.abc.com", std::string(), false },
2835 { "https:", "http://www.abc.com",
2836 "www.abc.com", std::string(), false },
2837 { "abc.com/", "http://www.abc.com",
2838 "www.abc.com", std::string(), false },
2839 { "http://www.abc.com/a", "http://www.abc.com",
2840 "http://www.abc.com", std::string(), false },
2841 { "http://www.abc.com", "https://www.abc.com",
2842 "https://www.abc.com", std::string(), false },
2843 { "http://abc.com", "ftp://abc.com",
2844 "ftp://abc.com", std::string(), false },
2845 { "https://www.abc.com", "http://www.abc.com",
2846 "www.abc.com", std::string(), false },
2847 { "ftp://abc.com", "http://abc.com",
2848 "abc.com", std::string(), false },
2850 // Do not inline matches with invalid input prefixes; trim http as needed.
2851 { "ttp", "http://www.abc.com",
2852 "www.abc.com", std::string(), false },
2853 { "://w", "http://www.abc.com",
2854 "www.abc.com", std::string(), false },
2855 { "ww.", "http://www.abc.com",
2856 "www.abc.com", std::string(), false },
2857 { ".ab", "http://www.abc.com",
2858 "www.abc.com", std::string(), false },
2859 { "bc", "http://www.abc.com",
2860 "www.abc.com", std::string(), false },
2861 { ".com", "http://www.abc.com",
2862 "www.abc.com", std::string(), false },
2864 // Do not inline matches that omit input domain labels; trim http as needed.
2865 { "www.a", "http://a.com",
2866 "a.com", std::string(), false },
2867 { "http://www.a", "http://a.com",
2868 "http://a.com", std::string(), false },
2869 { "www.a", "ftp://a.com",
2870 "ftp://a.com", std::string(), false },
2871 { "ftp://www.a", "ftp://a.com",
2872 "ftp://a.com", std::string(), false },
2874 // Input matching but with nothing to inline will not yield an offset, but
2875 // will be allowed to be default.
2876 { "abc.com", "http://www.abc.com",
2877 "www.abc.com", std::string(), true },
2878 { "http://www.abc.com", "http://www.abc.com",
2879 "http://www.abc.com", std::string(), true },
2881 // Inline matches when the input is a leading substring of the scheme.
2882 { "h", "http://www.abc.com",
2883 "http://www.abc.com", "ttp://www.abc.com", true },
2884 { "http", "http://www.abc.com",
2885 "http://www.abc.com", "://www.abc.com", true },
2887 // Inline matches when the input is a leading substring of the full URL.
2888 { "http:", "http://www.abc.com",
2889 "http://www.abc.com", "//www.abc.com", true },
2890 { "http://w", "http://www.abc.com",
2891 "http://www.abc.com", "ww.abc.com", true },
2892 { "http://www.", "http://www.abc.com",
2893 "http://www.abc.com", "abc.com", true },
2894 { "http://www.ab", "http://www.abc.com",
2895 "http://www.abc.com", "c.com", true },
2896 { "http://www.abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2897 "http://www.abc.com/path/file.htm?q=x#foo",
2898 "ath/file.htm?q=x#foo",
2899 true },
2900 { "http://abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
2901 "http://abc.com/path/file.htm?q=x#foo",
2902 "ath/file.htm?q=x#foo", true},
2904 // Inline matches with valid URLPrefixes; only trim "http://".
2905 { "w", "http://www.abc.com",
2906 "www.abc.com", "ww.abc.com", true },
2907 { "www.a", "http://www.abc.com",
2908 "www.abc.com", "bc.com", true },
2909 { "abc", "http://www.abc.com",
2910 "www.abc.com", ".com", true },
2911 { "abc.c", "http://www.abc.com",
2912 "www.abc.com", "om", true },
2913 { "abc.com/p", "http://www.abc.com/path/file.htm?q=x#foo",
2914 "www.abc.com/path/file.htm?q=x#foo",
2915 "ath/file.htm?q=x#foo", true },
2916 { "abc.com/p", "http://abc.com/path/file.htm?q=x#foo",
2917 "abc.com/path/file.htm?q=x#foo",
2918 "ath/file.htm?q=x#foo", true },
2920 // Inline matches using the maximal URLPrefix components.
2921 { "h", "http://help.com",
2922 "help.com", "elp.com", true },
2923 { "http", "http://http.com",
2924 "http.com", ".com", true },
2925 { "h", "http://www.help.com",
2926 "www.help.com", "elp.com", true },
2927 { "http", "http://www.http.com",
2928 "www.http.com", ".com", true },
2929 { "w", "http://www.www.com",
2930 "www.www.com", "ww.com", true },
2932 // Test similar behavior for the ftp and https schemes.
2933 { "ftp://www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2934 "ftp://www.abc.com/path/file.htm?q=x#foo",
2935 "c.com/path/file.htm?q=x#foo", true },
2936 { "www.ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2937 "ftp://www.abc.com/path/file.htm?q=x#foo",
2938 "c.com/path/file.htm?q=x#foo", true },
2939 { "ab", "ftp://www.abc.com/path/file.htm?q=x#foo",
2940 "ftp://www.abc.com/path/file.htm?q=x#foo",
2941 "c.com/path/file.htm?q=x#foo", true },
2942 { "ab", "ftp://abc.com/path/file.htm?q=x#foo",
2943 "ftp://abc.com/path/file.htm?q=x#foo",
2944 "c.com/path/file.htm?q=x#foo", true },
2945 { "https://www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
2946 "https://www.abc.com/path/file.htm?q=x#foo",
2947 "c.com/path/file.htm?q=x#foo", true },
2948 { "www.ab", "https://www.abc.com/path/file.htm?q=x#foo",
2949 "https://www.abc.com/path/file.htm?q=x#foo",
2950 "c.com/path/file.htm?q=x#foo", true },
2951 { "ab", "https://www.abc.com/path/file.htm?q=x#foo",
2952 "https://www.abc.com/path/file.htm?q=x#foo",
2953 "c.com/path/file.htm?q=x#foo", true },
2954 { "ab", "https://abc.com/path/file.htm?q=x#foo",
2955 "https://abc.com/path/file.htm?q=x#foo",
2956 "c.com/path/file.htm?q=x#foo", true },
2958 // Forced query input should inline and retain the "?" prefix.
2959 { "?http://www.ab", "http://www.abc.com",
2960 "?http://www.abc.com", "c.com", true },
2961 { "?www.ab", "http://www.abc.com",
2962 "?www.abc.com", "c.com", true },
2963 { "?ab", "http://www.abc.com",
2964 "?www.abc.com", "c.com", true },
2965 { "?abc.com", "http://www.abc.com",
2966 "?www.abc.com", "", true },
2969 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
2970 QueryForInput(ASCIIToUTF16(cases[i].input), false, false);
2971 AutocompleteMatch match(
2972 provider_->NavigationToMatch(SearchProvider::NavigationResult(
2973 *provider_.get(), GURL(cases[i].url), string16(), false, 0,
2974 false)));
2975 EXPECT_EQ(ASCIIToUTF16(cases[i].inline_autocompletion),
2976 match.inline_autocompletion);
2977 EXPECT_EQ(ASCIIToUTF16(cases[i].fill_into_edit), match.fill_into_edit);
2978 EXPECT_EQ(cases[i].allowed_to_be_default_match,
2979 match.allowed_to_be_default_match);
2983 // Verifies that "http://" is not trimmed for input that is a leading substring.
2984 TEST_F(SearchProviderTest, NavigationInlineSchemeSubstring) {
2985 const string16 input(ASCIIToUTF16("ht"));
2986 const string16 url(ASCIIToUTF16("http://a.com"));
2987 const SearchProvider::NavigationResult result(
2988 *provider_.get(), GURL(url), string16(), false, 0, false);
2990 // Check the offset and strings when inline autocompletion is allowed.
2991 QueryForInput(input, false, false);
2992 AutocompleteMatch match_inline(provider_->NavigationToMatch(result));
2993 EXPECT_EQ(url, match_inline.fill_into_edit);
2994 EXPECT_EQ(url.substr(2), match_inline.inline_autocompletion);
2995 EXPECT_TRUE(match_inline.allowed_to_be_default_match);
2996 EXPECT_EQ(url, match_inline.contents);
2998 // Check the same offset and strings when inline autocompletion is prevented.
2999 QueryForInput(input, true, false);
3000 AutocompleteMatch match_prevent(provider_->NavigationToMatch(result));
3001 EXPECT_EQ(url, match_prevent.fill_into_edit);
3002 EXPECT_TRUE(match_prevent.inline_autocompletion.empty());
3003 EXPECT_FALSE(match_prevent.allowed_to_be_default_match);
3004 EXPECT_EQ(url, match_prevent.contents);
3007 // Verifies that input "w" marks a more significant domain label than "www.".
3008 TEST_F(SearchProviderTest, NavigationInlineDomainClassify) {
3009 QueryForInput(ASCIIToUTF16("w"), false, false);
3010 AutocompleteMatch match(
3011 provider_->NavigationToMatch(SearchProvider::NavigationResult(
3012 *provider_.get(), GURL("http://www.wow.com"), string16(), false, 0,
3013 false)));
3014 EXPECT_EQ(ASCIIToUTF16("ow.com"), match.inline_autocompletion);
3015 EXPECT_TRUE(match.allowed_to_be_default_match);
3016 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.fill_into_edit);
3017 EXPECT_EQ(ASCIIToUTF16("www.wow.com"), match.contents);
3019 // Ensure that the match for input "w" is marked on "wow" and not "www".
3020 ASSERT_EQ(3U, match.contents_class.size());
3021 EXPECT_EQ(0U, match.contents_class[0].offset);
3022 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
3023 match.contents_class[0].style);
3024 EXPECT_EQ(4U, match.contents_class[1].offset);
3025 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL |
3026 AutocompleteMatch::ACMatchClassification::MATCH,
3027 match.contents_class[1].style);
3028 EXPECT_EQ(5U, match.contents_class[2].offset);
3029 EXPECT_EQ(AutocompleteMatch::ACMatchClassification::URL,
3030 match.contents_class[2].style);
3033 TEST_F(SearchProviderTest, RemoveStaleResultsTest) {
3034 // TODO(mpearson): Consider expanding this test to explicitly cover
3035 // testing staleness for keyword results.
3036 struct {
3037 const std::string omnibox_input;
3038 const int verbatim_relevance;
3039 // These cached suggestions should already be sorted.
3040 // The particular number 5 as the length of the array is
3041 // unimportant; it's merely enough cached results to fully test
3042 // the functioning of RemoveAllStaleResults().
3043 struct {
3044 const std::string suggestion;
3045 const bool is_navigation_result;
3046 const int relevance;
3047 // |expect_match| is true if this result should survive
3048 // RemoveAllStaleResults() filtering against |omnibox_input| below.
3049 const bool expect_match;
3050 } results[5];
3051 } cases[] = {
3052 // Simple case: multiple query suggestions and no navsuggestions.
3053 // All query suggestions score less than search-what-you-typed and
3054 // thus none should be filtered because none will appear first.
3055 { "x", 1300,
3056 { { "food", false, 1299, true },
3057 { "foobar", false, 1298, true },
3058 { "crazy", false, 1297, true },
3059 { "friend", false, 1296, true },
3060 { kNotApplicable, false, 0, false } } },
3062 // Similarly simple cases, but the query suggestion appears first.
3063 { "f", 1200,
3064 { { "food", false, 1299, true },
3065 { "foobar", false, 1298, true },
3066 { "crazy", false, 1297, true },
3067 { "friend", false, 1296, true },
3068 { kNotApplicable, false, 0, false } } },
3069 { "c", 1200,
3070 { { "food", false, 1299, false },
3071 { "foobar", false, 1298, false },
3072 { "crazy", false, 1297, true },
3073 { "friend", false, 1296, true },
3074 { kNotApplicable, false, 0, false } } },
3075 { "x", 1200,
3076 { { "food", false, 1299, false },
3077 { "foobar", false, 1298, false },
3078 { "crazy", false, 1297, false },
3079 { "friend", false, 1296, false },
3080 { kNotApplicable, false, 0, false } } },
3082 // The same sort of cases, just using a mix of queries and navsuggestions.
3083 { "x", 1300,
3084 { { "http://food.com/", true, 1299, true },
3085 { "foobar", false, 1298, true },
3086 { "http://crazy.com/", true, 1297, true },
3087 { "friend", false, 1296, true },
3088 { "http://friend.com/", true, 1295, true } } },
3089 { "f", 1200,
3090 { { "http://food.com/", true, 1299, true },
3091 { "foobar", false, 1298, true },
3092 { "http://crazy.com/", true, 1297, true },
3093 { "friend", false, 1296, true },
3094 { "http://friend.com/", true, 1295, true } } },
3095 { "c", 1200,
3096 { { "http://food.com/", true, 1299, false },
3097 { "foobar", false, 1298, false },
3098 { "http://crazy.com/", true, 1297, true },
3099 { "friend", false, 1296, true },
3100 { "http://friend.com/", true, 1295, true } } },
3101 { "x", 1200,
3102 { { "http://food.com/", true, 1299, false },
3103 { "foobar", false, 1298, false },
3104 { "http://crazy.com/", true, 1297, false },
3105 { "friend", false, 1296, false },
3106 { "http://friend.com/", true, 1295, false } } },
3108 // Run the three tests immediately above again, just with verbatim
3109 // suppressed. Note that in the last case, all results are filtered.
3110 // Because verbatim is also suppressed, SearchProvider will realize
3111 // in UpdateMatches() that it needs to restore verbatim to fulfill
3112 // its constraints. This restoration does not happen in
3113 // RemoveAllStaleResults() and hence is not tested here. This restoration
3114 // is tested in the DefaultFetcherSuggestRelevance test.
3115 { "f", 0,
3116 { { "http://food.com/", true, 1299, true },
3117 { "foobar", false, 1298, true },
3118 { "http://crazy.com/", true, 1297, true },
3119 { "friend", false, 1296, true },
3120 { "http://friend.com/", true, 1295, true } } },
3121 { "c", 0,
3122 { { "http://food.com/", true, 1299, false },
3123 { "foobar", false, 1298, false },
3124 { "http://crazy.com/", true, 1297, true },
3125 { "friend", false, 1296, true },
3126 { "http://friend.com/", true, 1295, true } } },
3127 { "x", 0,
3128 { { "http://food.com/", true, 1299, false },
3129 { "foobar", false, 1298, false },
3130 { "http://crazy.com/", true, 1297, false },
3131 { "friend", false, 1296, false },
3132 { "http://friend.com/", true, 1295, false } } },
3134 // The same sort of tests again, just with verbatim with a score
3135 // that would place it in between other suggestions.
3136 { "f", 1290,
3137 { { "http://food.com/", true, 1299, true },
3138 { "foobar", false, 1288, true },
3139 { "http://crazy.com/", true, 1277, true },
3140 { "friend", false, 1266, true },
3141 { "http://friend.com/", true, 1255, true } } },
3142 { "c", 1290,
3143 { { "http://food.com/", true, 1299, false },
3144 { "foobar", false, 1288, true },
3145 { "http://crazy.com/", true, 1277, true },
3146 { "friend", false, 1266, true },
3147 { "http://friend.com/", true, 1255, true } } },
3148 { "c", 1270,
3149 { { "http://food.com/", true, 1299, false },
3150 { "foobar", false, 1288, false },
3151 { "http://crazy.com/", true, 1277, true },
3152 { "friend", false, 1266, true },
3153 { "http://friend.com/", true, 1255, true } } },
3154 { "x", 1280,
3155 { { "http://food.com/", true, 1299, false },
3156 { "foobar", false, 1288, false },
3157 { "http://crazy.com/", true, 1277, true },
3158 { "friend", false, 1266, true },
3159 { "http://friend.com/", true, 1255, true } } },
3162 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3163 // Initialize cached results for this test case.
3164 provider_->default_results_.verbatim_relevance =
3165 cases[i].verbatim_relevance;
3166 provider_->default_results_.navigation_results.clear();
3167 provider_->default_results_.suggest_results.clear();
3168 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
3169 const std::string& suggestion = cases[i].results[j].suggestion;
3170 if (suggestion == kNotApplicable)
3171 break;
3172 if (cases[i].results[j].is_navigation_result) {
3173 provider_->default_results_.navigation_results.push_back(
3174 SearchProvider::NavigationResult(
3175 *provider_.get(), GURL(suggestion), string16(), false,
3176 cases[i].results[j].relevance, false));
3177 } else {
3178 provider_->default_results_.suggest_results.push_back(
3179 SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), string16(),
3180 string16(), std::string(), false,
3181 cases[i].results[j].relevance,
3182 false, false));
3186 provider_->input_ = AutocompleteInput(
3187 ASCIIToUTF16(cases[i].omnibox_input), string16::npos, string16(),
3188 GURL(), AutocompleteInput::INVALID_SPEC, false, false, true,
3189 AutocompleteInput::ALL_MATCHES);
3190 provider_->RemoveAllStaleResults();
3192 // Check cached results.
3193 SearchProvider::SuggestResults::const_iterator sug_it =
3194 provider_->default_results_.suggest_results.begin();
3195 const SearchProvider::SuggestResults::const_iterator sug_end =
3196 provider_->default_results_.suggest_results.end();
3197 SearchProvider::NavigationResults::const_iterator nav_it =
3198 provider_->default_results_.navigation_results.begin();
3199 const SearchProvider::NavigationResults::const_iterator nav_end =
3200 provider_->default_results_.navigation_results.end();
3201 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(cases[i].results); ++j) {
3202 const std::string& suggestion = cases[i].results[j].suggestion;
3203 if (suggestion == kNotApplicable)
3204 continue;
3205 if (!cases[i].results[j].expect_match)
3206 continue;
3207 if (cases[i].results[j].is_navigation_result) {
3208 ASSERT_NE(nav_end, nav_it) << "Failed to find " << suggestion;
3209 EXPECT_EQ(suggestion, nav_it->url().spec());
3210 ++nav_it;
3211 } else {
3212 ASSERT_NE(sug_end, sug_it) << "Failed to find " << suggestion;
3213 EXPECT_EQ(ASCIIToUTF16(suggestion), sug_it->suggestion());
3214 ++sug_it;
3217 EXPECT_EQ(sug_end, sug_it);
3218 EXPECT_EQ(nav_end, nav_it);
3222 #if !defined(OS_WIN)
3223 // Verify entity suggestion parsing.
3224 TEST_F(SearchProviderTest, ParseEntitySuggestion) {
3225 struct Match {
3226 std::string contents;
3227 std::string query_params;
3228 std::string fill_into_edit;
3229 AutocompleteMatchType::Type type;
3230 size_t classification_offsets[3];
3231 int classification_styles[3];
3233 const size_t invalid_offset = 10;
3234 const int invalid_style = -1;
3235 const Match kEmptyMatch = {
3236 kNotApplicable, kNotApplicable, kNotApplicable,
3237 AutocompleteMatchType::NUM_TYPES,
3238 { invalid_offset, invalid_offset, invalid_offset },
3239 { invalid_style, invalid_style, invalid_style } };
3241 struct {
3242 const std::string input_text;
3243 const std::string response_json;
3244 const Match matches[5];
3245 } cases[] = {
3246 // A query and an entity suggestion with different search terms.
3247 { "x",
3248 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
3249 " {\"google:suggestdetail\":[{},"
3250 " {\"a\":\"A\",\"dq\":\"yy\",\"q\":\"p=v\"}],"
3251 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3252 { { "x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3253 { 0, invalid_offset, invalid_offset },
3254 { ACMatchClassification::NONE, invalid_style, invalid_style } },
3255 { "xy", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST,
3256 { 0, 1, invalid_offset },
3257 { ACMatchClassification::NONE, ACMatchClassification::MATCH,
3258 invalid_style } },
3259 { "xy - A", "p=v", "yy", AutocompleteMatchType::SEARCH_SUGGEST,
3260 { 0, 1, 2 },
3261 { ACMatchClassification::NONE, ACMatchClassification::MATCH,
3262 ACMatchClassification::DIM } },
3263 kEmptyMatch,
3264 kEmptyMatch
3267 // A query and an entity suggestion with same search terms.
3268 { "x",
3269 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
3270 " {\"google:suggestdetail\":[{},"
3271 " {\"a\":\"A\",\"dq\":\"xy\",\"q\":\"p=v\"}],"
3272 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3273 { { "x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3274 { 0, invalid_offset, invalid_offset },
3275 { ACMatchClassification::NONE, invalid_style, invalid_style } },
3276 { "xy", "", "xy", AutocompleteMatchType::SEARCH_SUGGEST,
3277 { 0, 1, invalid_offset },
3278 { ACMatchClassification::NONE, ACMatchClassification::MATCH,
3279 invalid_style } },
3280 { "xy - A", "p=v", "xy", AutocompleteMatchType::SEARCH_SUGGEST,
3281 { 0, 1, 2 },
3282 { ACMatchClassification::NONE, ACMatchClassification::MATCH,
3283 ACMatchClassification::DIM } },
3284 kEmptyMatch,
3285 kEmptyMatch
3289 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3290 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3292 // Set up a default fetcher with provided results.
3293 net::TestURLFetcher* fetcher =
3294 test_factory_.GetFetcherByID(
3295 SearchProvider::kDefaultProviderURLFetcherID);
3296 ASSERT_TRUE(fetcher);
3297 fetcher->set_response_code(200);
3298 fetcher->SetResponseString(cases[i].response_json);
3299 fetcher->delegate()->OnURLFetchComplete(fetcher);
3301 RunTillProviderDone();
3303 const ACMatches& matches = provider_->matches();
3304 ASSERT_FALSE(matches.empty());
3306 SCOPED_TRACE("for input with json = " + cases[i].response_json);
3308 size_t j = 0;
3309 // Ensure that the returned matches equal the expectations.
3310 for (; j < matches.size(); ++j) {
3311 const Match& match = cases[i].matches[j];
3312 SCOPED_TRACE(" and match index: " + base::IntToString(j));
3313 EXPECT_EQ(match.contents,
3314 UTF16ToUTF8(matches[j].contents));
3315 EXPECT_EQ(match.query_params,
3316 matches[j].search_terms_args->suggest_query_params);
3317 EXPECT_EQ(match.fill_into_edit,
3318 UTF16ToUTF8(matches[j].fill_into_edit));
3319 EXPECT_EQ(match.type, matches[j].type);
3321 size_t k = 0;
3322 for (; k < matches[j].contents_class.size(); k++) {
3323 SCOPED_TRACE(" and contents class: " + base::IntToString(k));
3324 EXPECT_EQ(match.classification_offsets[k],
3325 matches[j].contents_class[k].offset);
3326 EXPECT_EQ(match.classification_styles[k],
3327 matches[j].contents_class[k].style);
3329 for (; k < ARRAYSIZE_UNSAFE(match.classification_offsets); k++) {
3330 SCOPED_TRACE(" and contents class: " + base::IntToString(k));
3331 EXPECT_EQ(match.classification_offsets[k], invalid_offset);
3332 EXPECT_EQ(match.classification_styles[k], invalid_style);
3335 // Ensure that no expected matches are missing.
3336 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
3337 SCOPED_TRACE(" and match index: " + base::IntToString(j));
3338 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3339 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
3340 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
3341 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3345 #endif // !defined(OS_WIN)
3347 TEST_F(SearchProviderTest, SearchHistorySuppressesEntitySuggestion) {
3348 struct Match {
3349 std::string contents;
3350 std::string query_params;
3351 std::string fill_into_edit;
3352 AutocompleteMatchType::Type type;
3354 const Match kEmptyMatch = { kNotApplicable, kNotApplicable, kNotApplicable,
3355 AutocompleteMatchType::NUM_TYPES};
3357 struct {
3358 const std::string input_text;
3359 const std::string history_search_term;
3360 const std::string response_json;
3361 const Match matches[5];
3362 } cases[] = {
3363 // Search history suppresses both query and entity suggestions.
3364 { "x", "xy",
3365 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
3366 " {\"google:suggestdetail\":[{},"
3367 " {\"a\":\"A\",\"dq\":\"xy\",\"q\":\"p=v\"}],"
3368 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3370 {"xy", "", "xy", AutocompleteMatchType::SEARCH_HISTORY},
3371 {"x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED},
3372 {"xy - A", "p=v", "xy", AutocompleteMatchType::SEARCH_SUGGEST},
3373 kEmptyMatch,
3374 kEmptyMatch,
3377 // Search history suppresses only query suggestion.
3378 { "x", "xyy",
3379 "[\"x\",[\"xy\", \"xy\"],[\"\",\"\"],[],"
3380 " {\"google:suggestdetail\":[{},"
3381 " {\"a\":\"A\",\"dq\":\"xyy\",\"q\":\"p=v\"}],"
3382 "\"google:suggesttype\":[\"QUERY\",\"ENTITY\"]}]",
3384 {"xyy", "", "xyy", AutocompleteMatchType::SEARCH_HISTORY},
3385 {"xy", "", "xy", AutocompleteMatchType::SEARCH_HISTORY},
3386 {"x", "", "x", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED},
3387 {"xy - A", "p=v", "xyy", AutocompleteMatchType::SEARCH_SUGGEST},
3388 kEmptyMatch,
3393 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3394 GURL term_url(AddSearchToHistory(
3395 default_t_url_, ASCIIToUTF16(cases[i].history_search_term), 10));
3396 profile_.BlockUntilHistoryProcessesPendingRequests();
3397 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3399 // Set up a default fetcher with provided results.
3400 net::TestURLFetcher* fetcher =
3401 test_factory_.GetFetcherByID(
3402 SearchProvider::kDefaultProviderURLFetcherID);
3403 ASSERT_TRUE(fetcher);
3404 fetcher->set_response_code(200);
3405 fetcher->SetResponseString(cases[i].response_json);
3406 fetcher->delegate()->OnURLFetchComplete(fetcher);
3408 RunTillProviderDone();
3410 const ACMatches& matches = provider_->matches();
3411 ASSERT_FALSE(matches.empty());
3412 SCOPED_TRACE("for case: " + base::IntToString(i));
3414 size_t j = 0;
3415 // Ensure that the returned matches equal the expectations.
3416 for (; j < matches.size(); ++j) {
3417 SCOPED_TRACE(" and match index: " + base::IntToString(j));
3418 EXPECT_EQ(cases[i].matches[j].contents,
3419 UTF16ToUTF8(matches[j].contents));
3420 EXPECT_EQ(cases[i].matches[j].query_params,
3421 matches[j].search_terms_args->suggest_query_params);
3422 EXPECT_EQ(cases[i].matches[j].fill_into_edit,
3423 UTF16ToUTF8(matches[j].fill_into_edit));
3424 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3426 // Ensure that no expected matches are missing.
3427 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
3428 SCOPED_TRACE(" and match index: " + base::IntToString(j));
3429 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3430 EXPECT_EQ(cases[i].matches[j].query_params, kNotApplicable);
3431 EXPECT_EQ(cases[i].matches[j].fill_into_edit, kNotApplicable);
3432 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3437 // A basic test that verifies the prefetch metadata parsing logic.
3438 TEST_F(SearchProviderTest, PrefetchMetadataParsing) {
3439 struct Match {
3440 std::string contents;
3441 bool allowed_to_be_prefetched;
3442 AutocompleteMatchType::Type type;
3443 bool from_keyword;
3445 const Match kEmptyMatch = { kNotApplicable,
3446 false,
3447 AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3448 false };
3450 struct {
3451 const std::string input_text;
3452 bool prefer_keyword_provider_results;
3453 const std::string default_provider_response_json;
3454 const std::string keyword_provider_response_json;
3455 const Match matches[5];
3456 } cases[] = {
3457 // Default provider response does not have prefetch details. Ensure that the
3458 // suggestions are not marked as prefetch query.
3459 { "a",
3460 false,
3461 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
3462 std::string(),
3463 { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3464 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3465 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3466 kEmptyMatch,
3467 kEmptyMatch
3470 // Ensure that default provider suggest response prefetch details are
3471 // parsed and recorded in AutocompleteMatch.
3472 { "ab",
3473 false,
3474 "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[],"
3475 "{\"google:clientdata\":{\"phi\": 0},"
3476 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"],"
3477 "\"google:suggestrelevance\":[999, 12, 1]}]",
3478 std::string(),
3479 { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3480 { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false },
3481 { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3482 { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3483 kEmptyMatch
3486 // Default provider suggest response has prefetch details.
3487 // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for
3488 // the same query string. Ensure that the prefetch details from
3489 // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match.
3490 { "ab",
3491 false,
3492 "[\"ab\",[\"ab\", \"http://ab.com\"],[],[],"
3493 "{\"google:clientdata\":{\"phi\": 0},"
3494 "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"],"
3495 "\"google:suggestrelevance\":[99, 98]}]",
3496 std::string(),
3497 { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3498 {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false },
3499 kEmptyMatch,
3500 kEmptyMatch,
3501 kEmptyMatch
3504 // Default provider response has prefetch details. We prefer keyword
3505 // provider results. Ensure that prefetch bit for a suggestion from the
3506 // default search provider does not get copied onto a higher-scoring match
3507 // for the same query string from the keyword provider.
3508 { "k a",
3509 true,
3510 "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0},"
3511 "\"google:suggesttype\":[\"QUERY\", \"QUERY\"],"
3512 "\"google:suggestrelevance\":[9, 12]}]",
3513 "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]",
3514 { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true},
3515 { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false },
3516 { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false },
3517 { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true },
3518 { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true }
3523 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3524 QueryForInput(ASCIIToUTF16(cases[i].input_text), false,
3525 cases[i].prefer_keyword_provider_results);
3527 // Set up a default fetcher with provided results.
3528 net::TestURLFetcher* fetcher =
3529 test_factory_.GetFetcherByID(
3530 SearchProvider::kDefaultProviderURLFetcherID);
3531 ASSERT_TRUE(fetcher);
3532 fetcher->set_response_code(200);
3533 fetcher->SetResponseString(cases[i].default_provider_response_json);
3534 fetcher->delegate()->OnURLFetchComplete(fetcher);
3536 if (cases[i].prefer_keyword_provider_results) {
3537 // Set up a keyword fetcher with provided results.
3538 net::TestURLFetcher* keyword_fetcher =
3539 test_factory_.GetFetcherByID(
3540 SearchProvider::kKeywordProviderURLFetcherID);
3541 ASSERT_TRUE(keyword_fetcher);
3542 keyword_fetcher->set_response_code(200);
3543 keyword_fetcher->SetResponseString(
3544 cases[i].keyword_provider_response_json);
3545 keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher);
3546 keyword_fetcher = NULL;
3549 RunTillProviderDone();
3551 const std::string description =
3552 "for input with json =" + cases[i].default_provider_response_json;
3553 const ACMatches& matches = provider_->matches();
3554 // The top match must inline and score as highly as calculated verbatim.
3555 ASSERT_FALSE(matches.empty());
3556 EXPECT_GE(matches[0].relevance, 1300);
3558 // Ensure that the returned matches equal the expectations.
3559 for (size_t j = 0; j < matches.size(); ++j) {
3560 SCOPED_TRACE(description);
3561 EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
3562 EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched,
3563 SearchProvider::ShouldPrefetch(matches[j]));
3564 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3565 EXPECT_EQ(cases[i].matches[j].from_keyword,
3566 matches[j].keyword == ASCIIToUTF16("k"));
3571 // A basic test that verifies that the XSSI guarded JSON response is parsed
3572 // correctly.
3573 TEST_F(SearchProviderTest, XSSIGuardedJSONParsing) {
3574 struct Match {
3575 std::string contents;
3576 AutocompleteMatchType::Type type;
3578 const Match kEmptyMatch = { kNotApplicable,
3579 AutocompleteMatchType::NUM_TYPES};
3581 struct {
3582 const std::string input_text;
3583 const std::string default_provider_response_json;
3584 const Match matches[4];
3585 } cases[] = {
3586 // No XSSI guard.
3587 { "a",
3588 "[\"a\",[\"b\", \"c\"],[],[],"
3589 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3590 "\"google:suggestrelevance\":[1, 2]}]",
3591 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3592 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3593 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3594 kEmptyMatch,
3597 // Standard XSSI guard - )]}'\n.
3598 { "a",
3599 ")]}'\n[\"a\",[\"b\", \"c\"],[],[],"
3600 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3601 "\"google:suggestrelevance\":[1, 2]}]",
3602 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3603 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3604 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3605 kEmptyMatch,
3608 // Modified XSSI guard - contains "[".
3609 { "a",
3610 ")]}'\n[)\"[\"a\",[\"b\", \"c\"],[],[],"
3611 "{\"google:suggesttype\":[\"QUERY\",\"QUERY\"],"
3612 "\"google:suggestrelevance\":[1, 2]}]",
3613 { { "a", AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED },
3614 { "c", AutocompleteMatchType::SEARCH_SUGGEST },
3615 { "b", AutocompleteMatchType::SEARCH_SUGGEST },
3616 kEmptyMatch,
3621 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) {
3622 ClearAllResults();
3623 QueryForInput(ASCIIToUTF16(cases[i].input_text), false, false);
3625 // Set up a default fetcher with provided results.
3626 net::TestURLFetcher* fetcher =
3627 test_factory_.GetFetcherByID(
3628 SearchProvider::kDefaultProviderURLFetcherID);
3629 ASSERT_TRUE(fetcher);
3630 fetcher->set_response_code(200);
3631 fetcher->SetResponseString(cases[i].default_provider_response_json);
3632 fetcher->delegate()->OnURLFetchComplete(fetcher);
3634 RunTillProviderDone();
3636 const ACMatches& matches = provider_->matches();
3637 // The top match must inline and score as highly as calculated verbatim.
3638 ASSERT_FALSE(matches.empty());
3639 EXPECT_GE(matches[0].relevance, 1300);
3641 SCOPED_TRACE("for case: " + base::IntToString(i));
3642 size_t j = 0;
3643 // Ensure that the returned matches equal the expectations.
3644 for (; j < matches.size(); ++j) {
3645 SCOPED_TRACE("and match: " + base::IntToString(j));
3646 EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents));
3647 EXPECT_EQ(cases[i].matches[j].type, matches[j].type);
3649 for (; j < ARRAYSIZE_UNSAFE(cases[i].matches); ++j) {
3650 SCOPED_TRACE("and match: " + base::IntToString(j));
3651 EXPECT_EQ(cases[i].matches[j].contents, kNotApplicable);
3652 EXPECT_EQ(cases[i].matches[j].type, AutocompleteMatchType::NUM_TYPES);
3658 TEST_F(SearchProviderTest, ReflectsBookmarkBarState) {
3659 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, false);
3660 string16 term = term1_.substr(0, term1_.length() - 1);
3661 QueryForInput(term, true, false);
3662 ASSERT_FALSE(provider_->matches().empty());
3663 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3664 provider_->matches()[0].type);
3665 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3666 EXPECT_FALSE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3668 profile_.GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true);
3669 term = term1_.substr(0, term1_.length() - 1);
3670 QueryForInput(term, true, false);
3671 ASSERT_FALSE(provider_->matches().empty());
3672 EXPECT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
3673 provider_->matches()[0].type);
3674 ASSERT_TRUE(provider_->matches()[0].search_terms_args != NULL);
3675 EXPECT_TRUE(provider_->matches()[0].search_terms_args->bookmark_bar_pinned);
3678 TEST_F(SearchProviderTest, CanSendURL) {
3679 TemplateURLData template_url_data;
3680 template_url_data.short_name = ASCIIToUTF16("t");
3681 template_url_data.SetURL("http://www.google.com/{searchTerms}");
3682 template_url_data.suggestions_url = "http://www.google.com/{searchTerms}";
3683 template_url_data.instant_url = "http://does/not/exist?strk=1";
3684 template_url_data.search_terms_replacement_key = "strk";
3685 template_url_data.id = SEARCH_ENGINE_GOOGLE;
3686 TemplateURL google_template_url(&profile_, template_url_data);
3688 // Create field trial.
3689 base::FieldTrial* field_trial = base::FieldTrialList::CreateFieldTrial(
3690 "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
3691 field_trial->group();
3693 // Not signed in.
3694 EXPECT_FALSE(SearchProvider::CanSendURL(
3695 GURL("http://www.google.com/search"),
3696 GURL("https://www.google.com/complete/search"), &google_template_url,
3697 AutocompleteInput::OTHER, &profile_));
3698 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(&profile_);
3699 signin->SetAuthenticatedUsername("test");
3701 // All conditions should be met.
3702 EXPECT_TRUE(SearchProvider::CanSendURL(
3703 GURL("http://www.google.com/search"),
3704 GURL("https://www.google.com/complete/search"), &google_template_url,
3705 AutocompleteInput::OTHER, &profile_));
3707 // Not in field trial.
3708 ResetFieldTrialList();
3709 EXPECT_FALSE(SearchProvider::CanSendURL(
3710 GURL("http://www.google.com/search"),
3711 GURL("https://www.google.com/complete/search"), &google_template_url,
3712 AutocompleteInput::OTHER, &profile_));
3713 field_trial = base::FieldTrialList::CreateFieldTrial(
3714 "AutocompleteDynamicTrial_2", "EnableZeroSuggest");
3715 field_trial->group();
3717 // Invalid page URL.
3718 EXPECT_FALSE(SearchProvider::CanSendURL(
3719 GURL("badpageurl"),
3720 GURL("https://www.google.com/complete/search"), &google_template_url,
3721 AutocompleteInput::OTHER, &profile_));
3723 // Invalid page classification.
3724 EXPECT_FALSE(SearchProvider::CanSendURL(
3725 GURL("http://www.google.com/search"),
3726 GURL("https://www.google.com/complete/search"), &google_template_url,
3727 AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS,
3728 &profile_));
3730 // Invalid page classification.
3731 EXPECT_FALSE(SearchProvider::CanSendURL(
3732 GURL("http://www.google.com/search"),
3733 GURL("https://www.google.com/complete/search"), &google_template_url,
3734 AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS,
3735 &profile_));
3737 // HTTPS page URL on same domain as provider.
3738 EXPECT_TRUE(SearchProvider::CanSendURL(
3739 GURL("https://www.google.com/search"),
3740 GURL("https://www.google.com/complete/search"),
3741 &google_template_url, AutocompleteInput::OTHER, &profile_));
3743 // Non-HTTP[S] page URL on same domain as provider.
3744 EXPECT_FALSE(SearchProvider::CanSendURL(
3745 GURL("ftp://www.google.com/search"),
3746 GURL("https://www.google.com/complete/search"), &google_template_url,
3747 AutocompleteInput::OTHER, &profile_));
3749 // Non-HTTP page URL on different domain.
3750 EXPECT_FALSE(SearchProvider::CanSendURL(
3751 GURL("https://www.notgoogle.com/search"),
3752 GURL("https://www.google.com/complete/search"), &google_template_url,
3753 AutocompleteInput::OTHER, &profile_));
3755 // Non-HTTPS provider.
3756 EXPECT_FALSE(SearchProvider::CanSendURL(
3757 GURL("http://www.google.com/search"),
3758 GURL("http://www.google.com/complete/search"), &google_template_url,
3759 AutocompleteInput::OTHER, &profile_));
3761 // Suggest disabled.
3762 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, false);
3763 EXPECT_FALSE(SearchProvider::CanSendURL(
3764 GURL("http://www.google.com/search"),
3765 GURL("https://www.google.com/complete/search"), &google_template_url,
3766 AutocompleteInput::OTHER, &profile_));
3767 profile_.GetPrefs()->SetBoolean(prefs::kSearchSuggestEnabled, true);
3769 // Incognito.
3770 EXPECT_FALSE(SearchProvider::CanSendURL(
3771 GURL("http://www.google.com/search"),
3772 GURL("https://www.google.com/complete/search"), &google_template_url,
3773 AutocompleteInput::OTHER, profile_.GetOffTheRecordProfile()));
3775 // Tab sync not enabled.
3776 profile_.GetPrefs()->SetBoolean(prefs::kSyncKeepEverythingSynced, false);
3777 profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, false);
3778 EXPECT_FALSE(SearchProvider::CanSendURL(
3779 GURL("http://www.google.com/search"),
3780 GURL("https://www.google.com/complete/search"), &google_template_url,
3781 AutocompleteInput::OTHER, &profile_));
3782 profile_.GetPrefs()->SetBoolean(prefs::kSyncTabs, true);
3784 // Tab sync is encrypted.
3785 ProfileSyncService* service =
3786 ProfileSyncServiceFactory::GetInstance()->GetForProfile(&profile_);
3787 syncer::ModelTypeSet encrypted_types = service->GetEncryptedDataTypes();
3788 encrypted_types.Put(syncer::SESSIONS);
3789 service->OnEncryptedTypesChanged(encrypted_types, false);
3790 EXPECT_FALSE(SearchProvider::CanSendURL(
3791 GURL("http://www.google.com/search"),
3792 GURL("https://www.google.com/complete/search"), &google_template_url,
3793 AutocompleteInput::OTHER, &profile_));
3794 encrypted_types.Remove(syncer::SESSIONS);
3795 service->OnEncryptedTypesChanged(encrypted_types, false);
3797 // Check that there were no side effects from previous tests.
3798 EXPECT_TRUE(SearchProvider::CanSendURL(
3799 GURL("http://www.google.com/search"),
3800 GURL("https://www.google.com/complete/search"), &google_template_url,
3801 AutocompleteInput::OTHER, &profile_));