1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/omnibox/base_search_provider.h"
6
7 #include "base/strings/string16.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "components/omnibox/autocomplete_match.h"
10 #include "components/omnibox/autocomplete_match_type.h"
11 #include "components/omnibox/autocomplete_provider_client.h"
12 #include "components/omnibox/autocomplete_scheme_classifier.h"
13 #include "components/omnibox/search_suggestion_parser.h"
14 #include "components/search_engines/search_terms_data.h"
15 #include "components/search_engines/template_url_service.h"
16 #include "components/search_engines/template_url_service_client.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 using testing::NiceMock;
21 using testing::Return;
22 using testing::_;
23
24 class MockAutocompleteProviderClient : public AutocompleteProviderClient {
25 public:
MockAutocompleteProviderClient()26 MockAutocompleteProviderClient() {}
27 MOCK_METHOD0(RequestContext, net::URLRequestContextGetter*());
28 MOCK_METHOD0(IsOffTheRecord, bool());
29 MOCK_METHOD0(AcceptLanguages, std::string());
30 MOCK_METHOD0(SearchSuggestEnabled, bool());
31 MOCK_METHOD0(ShowBookmarkBar, bool());
32 MOCK_METHOD0(SchemeClassifier, const AutocompleteSchemeClassifier&());
33 MOCK_METHOD6(
34 Classify,
35 void(const base::string16& text,
36 bool prefer_keyword,
37 bool allow_exact_keyword_match,
38 metrics::OmniboxEventProto::PageClassification page_classification,
39 AutocompleteMatch* match,
40 GURL* alternate_nav_url));
41 MOCK_METHOD0(InMemoryDatabase, history::URLDatabase*());
42 MOCK_METHOD2(DeleteMatchingURLsForKeywordFromHistory,
43 void(history::KeywordID keyword_id, const base::string16& term));
44 MOCK_METHOD0(TabSyncEnabledAndUnencrypted, bool());
45 MOCK_METHOD1(PrefetchImage, void(const GURL& url));
46
47 private:
48 DISALLOW_COPY_AND_ASSIGN(MockAutocompleteProviderClient);
49 };
50
51 class TestBaseSearchProvider : public BaseSearchProvider {
52 public:
53 typedef BaseSearchProvider::MatchMap MatchMap;
54
55 // Note: Takes ownership of client. scoped_ptr<> would be the right way to
56 // express that, but NiceMock<> can't forward a scoped_ptr.
TestBaseSearchProvider(TemplateURLService * template_url_service,AutocompleteProviderClient * client,AutocompleteProvider::Type type)57 TestBaseSearchProvider(TemplateURLService* template_url_service,
58 AutocompleteProviderClient* client,
59 AutocompleteProvider::Type type)
60 : BaseSearchProvider(template_url_service,
61 scoped_ptr<AutocompleteProviderClient>(client),
62 type) {}
63 MOCK_METHOD1(DeleteMatch, void(const AutocompleteMatch& match));
64 MOCK_CONST_METHOD1(AddProviderInfo, void(ProvidersInfo* provider_info));
65 MOCK_CONST_METHOD1(GetTemplateURL, const TemplateURL*(bool is_keyword));
66 MOCK_CONST_METHOD1(GetInput, const AutocompleteInput(bool is_keyword));
67 MOCK_CONST_METHOD1(ShouldAppendExtraParams,
68 bool(const SearchSuggestionParser::SuggestResult& result));
69 MOCK_METHOD1(RecordDeletionResult, void(bool success));
70
71 MOCK_METHOD2(Start,
72 void(const AutocompleteInput& input, bool minimal_changes));
AddMatchToMap(const SearchSuggestionParser::SuggestResult & result,const std::string & metadata,int accepted_suggestion,bool mark_as_deletable,bool in_keyword_mode,MatchMap * map)73 void AddMatchToMap(const SearchSuggestionParser::SuggestResult& result,
74 const std::string& metadata,
75 int accepted_suggestion,
76 bool mark_as_deletable,
77 bool in_keyword_mode,
78 MatchMap* map) {
79 BaseSearchProvider::AddMatchToMap(result,
80 metadata,
81 accepted_suggestion,
82 mark_as_deletable,
83 in_keyword_mode,
84 map);
85 }
86
87 protected:
~TestBaseSearchProvider()88 virtual ~TestBaseSearchProvider() {}
89
90 private:
91 DISALLOW_COPY_AND_ASSIGN(TestBaseSearchProvider);
92 };
93
94 class BaseSearchProviderTest : public testing::Test {
95 public:
~BaseSearchProviderTest()96 virtual ~BaseSearchProviderTest() {}
97
98 protected:
SetUp()99 virtual void SetUp() {
100 service_.reset(
101 new TemplateURLService(NULL,
102 scoped_ptr<SearchTermsData>(new SearchTermsData),
103 NULL,
104 scoped_ptr<TemplateURLServiceClient>(),
105 NULL,
106 NULL,
107 base::Closure()));
108 provider_ = new NiceMock<TestBaseSearchProvider>(
109 service_.get(),
110 new NiceMock<MockAutocompleteProviderClient>,
111 AutocompleteProvider::TYPE_SEARCH);
112 }
113
114 scoped_refptr<NiceMock<TestBaseSearchProvider> > provider_;
115 scoped_ptr<TemplateURLService> service_;
116 };
117
TEST_F(BaseSearchProviderTest,PreserveAnswersWhenDeduplicating)118 TEST_F(BaseSearchProviderTest, PreserveAnswersWhenDeduplicating) {
119 TemplateURLData data;
120 data.SetURL("http://foo.com/url?bar={searchTerms}");
121 scoped_ptr<TemplateURL> template_url(new TemplateURL(data));
122
123 TestBaseSearchProvider::MatchMap map;
124 base::string16 query = base::ASCIIToUTF16("weather los angeles");
125 base::string16 answer_contents = base::ASCIIToUTF16("some answer content");
126 base::string16 answer_type = base::ASCIIToUTF16("2334");
127
128 EXPECT_CALL(*provider_, GetInput(_))
129 .WillRepeatedly(Return(AutocompleteInput()));
130 EXPECT_CALL(*provider_, GetTemplateURL(_))
131 .WillRepeatedly(Return(template_url.get()));
132
133 SearchSuggestionParser::SuggestResult more_relevant(
134 query, AutocompleteMatchType::SEARCH_HISTORY, query, base::string16(),
135 base::string16(), base::string16(), base::string16(), std::string(),
136 std::string(), false, 1300, true, false, query);
137 provider_->AddMatchToMap(
138 more_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
139 false, false, &map);
140
141 SearchSuggestionParser::SuggestResult less_relevant(
142 query, AutocompleteMatchType::SEARCH_SUGGEST, query, base::string16(),
143 base::string16(), answer_contents, answer_type, std::string(),
144 std::string(), false, 850, true, false, query);
145 provider_->AddMatchToMap(
146 less_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
147 false, false, &map);
148
149 ASSERT_EQ(1U, map.size());
150 AutocompleteMatch match = map.begin()->second;
151 ASSERT_EQ(1U, match.duplicate_matches.size());
152 AutocompleteMatch duplicate = match.duplicate_matches[0];
153
154 EXPECT_EQ(answer_contents, match.answer_contents);
155 EXPECT_EQ(answer_type, match.answer_type);
156 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, match.type);
157 EXPECT_EQ(1300, match.relevance);
158
159 EXPECT_EQ(answer_contents, duplicate.answer_contents);
160 EXPECT_EQ(answer_type, duplicate.answer_type);
161 EXPECT_EQ(AutocompleteMatchType::SEARCH_SUGGEST, duplicate.type);
162 EXPECT_EQ(850, duplicate.relevance);
163
164 // Ensure answers are not copied over existing answers.
165 map.clear();
166 base::string16 answer_contents2 = base::ASCIIToUTF16("different answer");
167 more_relevant = SearchSuggestionParser::SuggestResult(
168 query, AutocompleteMatchType::SEARCH_HISTORY, query, base::string16(),
169 base::string16(), answer_contents2, answer_type, std::string(),
170 std::string(), false, 1300, true, false, query);
171 provider_->AddMatchToMap(
172 more_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
173 false, false, &map);
174 provider_->AddMatchToMap(
175 less_relevant, std::string(), TemplateURLRef::NO_SUGGESTION_CHOSEN,
176 false, false, &map);
177 ASSERT_EQ(1U, map.size());
178 match = map.begin()->second;
179 ASSERT_EQ(1U, match.duplicate_matches.size());
180 duplicate = match.duplicate_matches[0];
181
182 EXPECT_EQ(answer_contents2, match.answer_contents);
183 EXPECT_EQ(answer_type, match.answer_type);
184 EXPECT_EQ(AutocompleteMatchType::SEARCH_HISTORY, match.type);
185 EXPECT_EQ(1300, match.relevance);
186
187 EXPECT_EQ(answer_contents, duplicate.answer_contents);
188 EXPECT_EQ(answer_type, duplicate.answer_type);
189 EXPECT_EQ(AutocompleteMatchType::SEARCH_SUGGEST, duplicate.type);
190 EXPECT_EQ(850, duplicate.relevance);
191
192 }
193