OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/ui/app_list/search/app_search_provider.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <memory> | |
10 #include <string> | |
11 #include <utility> | |
12 | |
13 #include "base/macros.h" | |
14 #include "base/metrics/field_trial.h" | |
15 #include "base/metrics/field_trial_params.h" | |
16 #include "base/strings/utf_string_conversions.h" | |
17 #include "base/test/scoped_feature_list.h" | |
18 #include "chrome/browser/ui/app_list/app_list_test_util.h" | |
19 #include "chrome/browser/ui/app_list/search/answer_card/answer_card_search_provi der.h" | |
20 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h" | |
21 #include "chrome/test/base/testing_profile.h" | |
22 #include "content/test/mock_navigation_handle.h" | |
23 #include "net/http/http_response_headers.h" | |
24 #include "testing/gtest/include/gtest/gtest.h" | |
25 #include "ui/app_list/app_list_features.h" | |
26 #include "ui/app_list/app_list_model.h" | |
27 #include "ui/app_list/search_result.h" | |
28 | |
29 using ::testing::_; | |
30 using ::testing::Return; | |
31 using ::testing::ReturnRef; | |
32 | |
33 namespace app_list { | |
34 namespace test { | |
35 | |
36 namespace { | |
37 | |
38 views::View* const kView = reinterpret_cast<views::View*>(123456); | |
xiyuan
2017/06/22 16:43:21
Can we create a real views::View owned by AnswerCa
vadimt
2017/06/22 19:48:03
Done.
| |
39 constexpr char kCatCardId[] = "http://meow.org/meow"; | |
40 constexpr char kCatCardTitle[] = "Cat is a furry beast."; | |
41 | |
42 class MockAnswerCardContents : public AnswerCardContents { | |
43 public: | |
44 MockAnswerCardContents() {} | |
45 | |
46 // AnswerCardContents overrides: | |
47 MOCK_METHOD1(LoadURL, void(const GURL& url)); | |
48 MOCK_CONST_METHOD0(IsLoading, bool()); | |
49 MOCK_METHOD0(GetView, views::View*()); | |
50 | |
51 private: | |
52 DISALLOW_COPY_AND_ASSIGN(MockAnswerCardContents); | |
53 }; | |
54 | |
55 gfx::Size GetMaxValidCardSize() { | |
56 return gfx::Size(features::AnswerCardMaxWidth(), | |
57 features::AnswerCardMaxHeight()); | |
58 } | |
59 | |
60 } // namespace | |
61 | |
62 class AnswerCardSearchProviderTest : public AppListTestBase { | |
63 public: | |
64 AnswerCardSearchProviderTest() : field_trial_list_(nullptr) {} | |
65 | |
66 void SetMockHeaders(std::string has_result, | |
67 std::string open_url, | |
68 std::string title) { | |
69 headers_->RemoveHeader("SearchAnswer-HasResult"); | |
70 headers_->RemoveHeader("SearchAnswer-OpenResultUrl"); | |
71 headers_->RemoveHeader("SearchAnswer-Title"); | |
72 | |
73 if (!has_result.empty()) | |
74 headers_->AddHeader("SearchAnswer-HasResult: " + has_result); | |
75 if (!open_url.empty()) | |
76 headers_->AddHeader("SearchAnswer-OpenResultUrl: " + open_url); | |
77 if (!title.empty()) | |
78 headers_->AddHeader("SearchAnswer-Title: " + title); | |
79 } | |
80 | |
81 void TestHeadersParsing(std::string has_result, | |
82 std::string open_url, | |
83 std::string title, | |
84 std::size_t expected_result_count) { | |
85 SetMockHeaders(has_result, open_url, title); | |
86 | |
87 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
88 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
89 | |
90 GURL url("http://beasts.org/search?q=cat"); | |
91 EXPECT_CALL(*navigation_handle_.get(), GetURL()).WillOnce(ReturnRef(url)); | |
92 provider()->DidFinishNavigation(navigation_handle_.get()); | |
93 | |
94 provider()->DidStopLoading(); | |
95 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
96 | |
97 EXPECT_EQ(expected_result_count, results().size()); | |
xiyuan
2017/06/22 16:43:21
We might need a
testing::Mock::VerifyAndClearE
vadimt
2017/06/22 19:48:03
Done.
| |
98 } | |
99 | |
100 AppListModel* model() const { return model_.get(); } | |
101 | |
102 const SearchProvider::Results& results() { return provider()->results(); } | |
103 | |
104 MockAnswerCardContents* contents() const { return contents_; } | |
105 | |
106 AnswerCardSearchProvider* provider() const { return provider_.get(); } | |
107 | |
108 content::MockNavigationHandle* navigation_handle() const { | |
109 return navigation_handle_.get(); | |
110 } | |
111 | |
112 // AppListTestBase overrides: | |
113 void SetUp() override { | |
114 AppListTestBase::SetUp(); | |
115 | |
116 model_ = base::MakeUnique<app_list::AppListModel>(); | |
117 model_->SetSearchEngineIsGoogle(true); | |
118 | |
119 controller_ = base::MakeUnique<::test::TestAppListControllerDelegate>(); | |
120 | |
121 // Set up card server URL. | |
122 std::map<std::string, std::string> params; | |
123 params["ServerUrl"] = "http://beasts.org/search"; | |
124 base::AssociateFieldTrialParams("TestTrial", "TestGroup", params); | |
125 scoped_refptr<base::FieldTrial> trial = | |
126 base::FieldTrialList::CreateFieldTrial("TestTrial", "TestGroup"); | |
127 std::unique_ptr<base::FeatureList> feature_list = | |
128 base::MakeUnique<base::FeatureList>(); | |
129 feature_list->RegisterFieldTrialOverride( | |
130 features::kEnableAnswerCard.name, | |
131 base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get()); | |
132 scoped_feature_list_.InitWithFeatureList(std::move(feature_list)); | |
133 | |
134 contents_ = new MockAnswerCardContents; | |
135 std::unique_ptr<AnswerCardContents> contents(contents_); | |
xiyuan
2017/06/22 16:43:22
Would presubmit script complains about using std::
vadimt
2017/06/22 19:48:03
No, presubmit is fine.
| |
136 // Provider will own the MockAnswerCardContents instance. | |
137 provider_ = base::MakeUnique<AnswerCardSearchProvider>( | |
138 profile_.get(), model_.get(), nullptr, std::move(contents)); | |
139 | |
140 headers_ = new net::HttpResponseHeaders(""); | |
141 SetMockHeaders("true", kCatCardId, kCatCardTitle); | |
142 | |
143 navigation_handle_ = base::MakeUnique<content::MockNavigationHandle>(); | |
144 ON_CALL(*navigation_handle_.get(), HasCommitted()) | |
145 .WillByDefault(Return(true)); | |
146 ON_CALL(*navigation_handle_.get(), IsErrorPage()) | |
147 .WillByDefault(Return(false)); | |
148 ON_CALL(*navigation_handle_.get(), IsInMainFrame()) | |
149 .WillByDefault(Return(true)); | |
150 ON_CALL(*navigation_handle_.get(), GetResponseHeaders()) | |
151 .WillByDefault(Return(headers_.get())); | |
152 | |
153 ON_CALL(*contents_, GetView()).WillByDefault(Return(kView)); | |
154 } | |
155 | |
156 private: | |
157 std::unique_ptr<app_list::AppListModel> model_; | |
158 std::unique_ptr<AnswerCardSearchProvider> provider_; | |
159 std::unique_ptr<::test::TestAppListControllerDelegate> controller_; | |
160 MockAnswerCardContents* contents_ = nullptr; // Unowned. | |
161 base::FieldTrialList field_trial_list_; | |
162 base::test::ScopedFeatureList scoped_feature_list_; | |
163 scoped_refptr<net::HttpResponseHeaders> headers_; | |
164 std::unique_ptr<content::MockNavigationHandle> navigation_handle_; | |
165 | |
166 DISALLOW_COPY_AND_ASSIGN(AnswerCardSearchProviderTest); | |
167 }; | |
168 | |
169 // Basic event sequence. | |
170 TEST_F(AnswerCardSearchProviderTest, Basic) { | |
171 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
172 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
173 GURL url("http://beasts.org/search?q=cat"); | |
174 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
175 provider()->DidFinishNavigation(navigation_handle()); | |
176 provider()->DidStopLoading(); | |
177 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
178 | |
179 EXPECT_EQ(1UL, results().size()); | |
180 SearchResult* result = results()[0].get(); | |
181 EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); | |
182 EXPECT_EQ(kCatCardId, result->id()); | |
183 EXPECT_EQ(1, result->relevance()); | |
184 EXPECT_EQ(kView, result->view()); | |
185 EXPECT_EQ(base::UTF8ToUTF16(kCatCardTitle), result->title()); | |
186 } | |
187 | |
188 // Voice queries are ignored. | |
189 TEST_F(AnswerCardSearchProviderTest, VoiceQuery) { | |
190 EXPECT_CALL(*contents(), LoadURL(_)).Times(0); | |
191 provider()->Start(true, base::UTF8ToUTF16("cat")); | |
192 } | |
193 | |
194 // Queries to non-Google search engines are ignored. | |
195 TEST_F(AnswerCardSearchProviderTest, NotGoogle) { | |
196 model()->SetSearchEngineIsGoogle(false); | |
197 EXPECT_CALL(*contents(), LoadURL(_)).Times(0); | |
198 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
199 } | |
200 | |
201 // Zero-query is ignored. | |
202 TEST_F(AnswerCardSearchProviderTest, EmptyQuery) { | |
203 EXPECT_CALL(*contents(), LoadURL(_)).Times(0); | |
204 provider()->Start(false, base::UTF8ToUTF16("")); | |
205 } | |
206 | |
207 // Two queries, the second produces a card of exactly same size, so | |
208 // UpdatePreferredSize() doesn't come. The second query should still produce a | |
209 // result. | |
210 TEST_F(AnswerCardSearchProviderTest, TwoResultsSameSize) { | |
211 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
212 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
213 GURL url("http://beasts.org/search?q=cat"); | |
214 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
215 provider()->DidFinishNavigation(navigation_handle()); | |
216 provider()->DidStopLoading(); | |
217 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
218 | |
219 EXPECT_EQ(1UL, results().size()); | |
220 SearchResult* result = results()[0].get(); | |
221 EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); | |
222 EXPECT_EQ(kCatCardId, result->id()); | |
223 EXPECT_EQ(1, result->relevance()); | |
224 EXPECT_EQ(kView, result->view()); | |
225 EXPECT_EQ(base::UTF8ToUTF16(kCatCardTitle), result->title()); | |
226 | |
227 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=dog"))); | |
228 provider()->Start(false, base::UTF8ToUTF16("dog")); | |
229 EXPECT_EQ(0UL, results().size()); | |
230 | |
231 url = GURL("http://beasts.org/search?q=dog"); | |
232 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
233 SetMockHeaders("true", "http://woof.org/woof", "Dog is a friendly beast."); | |
234 provider()->DidFinishNavigation(navigation_handle()); | |
235 provider()->DidStopLoading(); | |
236 // No UpdatePreferredSize(). | |
237 | |
238 EXPECT_EQ(1UL, results().size()); | |
239 result = results()[0].get(); | |
240 EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); | |
241 EXPECT_EQ("http://woof.org/woof", result->id()); | |
242 EXPECT_EQ(1, result->relevance()); | |
243 EXPECT_EQ(kView, result->view()); | |
244 EXPECT_EQ(base::UTF8ToUTF16("Dog is a friendly beast."), result->title()); | |
245 } | |
246 | |
247 // User enters a query character by character, so that each next query generates | |
248 // a web request while the previous one is still in progress. Only the last | |
249 // query should produce a result. | |
250 TEST_F(AnswerCardSearchProviderTest, InterruptedRequest) { | |
251 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=c"))); | |
252 provider()->Start(false, base::UTF8ToUTF16("c")); | |
253 EXPECT_EQ(0UL, results().size()); | |
254 | |
255 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=ca"))); | |
256 provider()->Start(false, base::UTF8ToUTF16("ca")); | |
257 EXPECT_EQ(0UL, results().size()); | |
258 | |
259 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
260 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
261 EXPECT_EQ(0UL, results().size()); | |
262 | |
263 SetMockHeaders("true", "http://c-meow", "Title c"); | |
264 GURL url = GURL("http://beasts.org/search?q=c"); | |
265 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
266 provider()->DidFinishNavigation(navigation_handle()); | |
267 provider()->DidStopLoading(); | |
268 EXPECT_EQ(0UL, results().size()); | |
269 | |
270 SetMockHeaders("true", "http://ca-meow", "Title ca"); | |
271 url = GURL("http://beasts.org/search?q=ca"); | |
272 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
273 provider()->DidFinishNavigation(navigation_handle()); | |
274 provider()->DidStopLoading(); | |
275 provider()->UpdatePreferredSize(gfx::Size(1, 1)); | |
276 EXPECT_EQ(0UL, results().size()); | |
277 | |
278 SetMockHeaders("true", kCatCardId, kCatCardTitle); | |
279 url = GURL("http://beasts.org/search?q=cat"); | |
280 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
281 provider()->DidFinishNavigation(navigation_handle()); | |
282 provider()->DidStopLoading(); | |
283 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
284 EXPECT_EQ(1UL, results().size()); | |
285 | |
286 SearchResult* result = results()[0].get(); | |
287 EXPECT_EQ(SearchResult::DISPLAY_CARD, result->display_type()); | |
288 EXPECT_EQ(kCatCardId, result->id()); | |
289 EXPECT_EQ(base::UTF8ToUTF16(kCatCardTitle), result->title()); | |
290 } | |
291 | |
292 // Due to, for example, JS activity in the card, it can change its size after | |
293 // loading. We should hide the result while its size if larger than the allowed | |
294 // maximum. | |
295 TEST_F(AnswerCardSearchProviderTest, ChangingSize) { | |
296 EXPECT_CALL(*contents(), LoadURL(GURL("http://beasts.org/search?q=cat"))); | |
297 provider()->Start(false, base::UTF8ToUTF16("cat")); | |
298 GURL url("http://beasts.org/search?q=cat"); | |
299 EXPECT_CALL(*navigation_handle(), GetURL()).WillOnce(ReturnRef(url)); | |
300 provider()->DidFinishNavigation(navigation_handle()); | |
301 provider()->UpdatePreferredSize(gfx::Size(features::AnswerCardMaxWidth() + 1, | |
302 features::AnswerCardMaxHeight())); | |
303 provider()->DidStopLoading(); | |
304 EXPECT_EQ(0UL, results().size()); | |
305 | |
306 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
307 EXPECT_EQ(1UL, results().size()); | |
308 | |
309 provider()->UpdatePreferredSize(gfx::Size( | |
310 features::AnswerCardMaxWidth(), features::AnswerCardMaxHeight() + 1)); | |
311 EXPECT_EQ(0UL, results().size()); | |
312 | |
313 provider()->UpdatePreferredSize(GetMaxValidCardSize()); | |
314 EXPECT_EQ(1UL, results().size()); | |
315 } | |
316 | |
317 // The result is generated only when all headers exist, and indicate presence of | |
318 // the result. | |
319 TEST_F(AnswerCardSearchProviderTest, Headers) { | |
320 TestHeadersParsing("true", kCatCardId, kCatCardTitle, 1UL); | |
321 TestHeadersParsing("false", kCatCardId, kCatCardTitle, 0UL); | |
322 TestHeadersParsing("", kCatCardId, kCatCardTitle, 0UL); | |
323 TestHeadersParsing("true", "", kCatCardTitle, 0UL); | |
324 TestHeadersParsing("true", kCatCardId, "", 0UL); | |
325 TestHeadersParsing("true", kCatCardId, kCatCardTitle, 1UL); | |
326 } | |
327 | |
328 } // namespace test | |
329 } // namespace app_list | |
OLD | NEW |