1 // Copyright 2015 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/android/contextualsearch/contextual_search_delegate.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "chrome/browser/android/contextualsearch/contextual_search_context.h"
12 #include "components/search_engines/template_url_service.h"
13 #include "net/url_request/test_url_fetcher_factory.h"
14 #include "net/url_request/url_request_test_util.h"
15 #include "testing/gtest/include/gtest/gtest.h"
17 using base::ListValue
;
21 const char kSomeSpecificBasePage
[] = "http://some.specific.host.name.com";
25 class ContextualSearchDelegateTest
: public testing::Test
{
27 ContextualSearchDelegateTest() {}
28 ~ContextualSearchDelegateTest() override
{}
31 void SetUp() override
{
33 new net::TestURLRequestContextGetter(io_message_loop_
.task_runner());
34 template_url_service_
.reset(CreateTemplateURLService());
35 delegate_
.reset(new ContextualSearchDelegate(
36 request_context_
.get(), template_url_service_
.get(),
38 &ContextualSearchDelegateTest::recordSearchTermResolutionResponse
,
39 base::Unretained(this)),
40 base::Bind(&ContextualSearchDelegateTest::recordSurroundingText
,
41 base::Unretained(this)),
42 base::Bind(&ContextualSearchDelegateTest::recordIcingSelectionAvailable
,
43 base::Unretained(this))));
46 void TearDown() override
{
50 search_term_
= "invalid";
51 display_text_
= "unknown";
54 TemplateURLService
* CreateTemplateURLService() {
55 // Set a default search provider that supports Contextual Search.
57 data
.SetURL("https://foobar.com/url?bar={searchTerms}");
58 data
.contextual_search_url
= "https://foobar.com/_/contextualsearch?"
59 "{google:contextualSearchVersion}{google:contextualSearchContextData}";
60 TemplateURL
* template_url
= new TemplateURL(data
);
61 // Takes ownership of |template_url|.
62 TemplateURLService
* template_url_service
= new TemplateURLService(NULL
, 0);
63 template_url_service
->Add(template_url
);
64 template_url_service
->SetUserSelectedDefaultSearchProvider(template_url
);
65 return template_url_service
;
68 void CreateDefaultSearchContextAndRequestSearchTerm() {
69 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
70 CreateSearchContextAndRequestSearchTerm("Barack Obama", surrounding
, 0, 6);
73 void CreateSearchContextAndRequestSearchTerm(
74 const std::string
& selected_text
,
75 const base::string16
& surrounding_text
,
78 test_context_
= new ContextualSearchContext(
79 selected_text
, true, GURL(kSomeSpecificBasePage
), "utf-8");
80 // ContextualSearchDelegate class takes ownership of the context.
81 delegate_
->set_context_for_testing(test_context_
);
83 test_context_
->start_offset
= start_offset
;
84 test_context_
->end_offset
= end_offset
;
85 test_context_
->surrounding_text
= surrounding_text
;
86 delegate_
->ContinueSearchTermResolutionRequest();
87 fetcher_
= test_factory_
.GetFetcherByID(
88 ContextualSearchDelegate::kContextualSearchURLFetcherID
);
89 ASSERT_TRUE(fetcher_
);
90 ASSERT_TRUE(fetcher());
93 void SetResponseStringAndFetch(const std::string
& selected_text
,
94 const std::string
& mentions_start
,
95 const std::string
& mentions_end
) {
96 fetcher()->set_response_code(200);
97 fetcher()->SetResponseString(
99 "{\"mid\":\"/m/02mjmr\", \"search_term\":\"obama\","
100 "\"info_text\":\"44th U.S. President\","
101 "\"display_text\":\"Barack Obama\","
102 "\"mentions\":[" + mentions_start
+ ","+ mentions_end
+ "],"
103 "\"selected_text\":\"" + selected_text
+ "\","
104 "\"resolved_term\":\"barack obama\"}");
105 fetcher()->delegate()->OnURLFetchComplete(fetcher());
108 void SetSurroundingContext(const base::string16
& surrounding_text
,
111 test_context_
= new ContextualSearchContext(
112 "Bogus", true, GURL(kSomeSpecificBasePage
), "utf-8");
113 test_context_
->surrounding_text
= surrounding_text
;
114 test_context_
->start_offset
= start_offset
;
115 test_context_
->end_offset
= end_offset
;
116 // ContextualSearchDelegate class takes ownership of the context.
117 delegate_
->set_context_for_testing(test_context_
);
120 bool DoesRequestContainOurSpecificBasePage() {
121 return fetcher()->GetOriginalURL().spec().find(
122 specific_base_page_URL_escaped()) != std::string::npos
;
125 std::string
specific_base_page_URL_escaped() {
126 return net::EscapeQueryParamValue(kSomeSpecificBasePage
, true);
129 net::TestURLFetcher
* fetcher() { return fetcher_
; }
130 bool is_invalid() { return is_invalid_
; }
131 int response_code() { return response_code_
; }
132 std::string
search_term() { return search_term_
; }
133 std::string
display_text() { return display_text_
; }
134 std::string
alternate_term() { return alternate_term_
; }
135 bool do_prevent_preload() { return prevent_preload_
; }
136 std::string
before_text() { return before_text_
; }
137 std::string
after_text() { return after_text_
; }
138 int start_adjust() { return start_adjust_
; }
139 int end_adjust() { return end_adjust_
; }
141 // The delegate under test.
142 scoped_ptr
<ContextualSearchDelegate
> delegate_
;
145 void recordSearchTermResolutionResponse(bool is_invalid
,
147 const std::string
& search_term
,
148 const std::string
& display_text
,
149 const std::string
& alternate_term
,
150 bool prevent_preload
,
153 is_invalid_
= is_invalid
;
154 response_code_
= response_code
;
155 search_term_
= search_term
;
156 display_text_
= display_text
;
157 alternate_term_
= alternate_term
;
158 prevent_preload_
= prevent_preload
;
159 start_adjust_
= start_adjust
;
160 end_adjust_
= end_adjust
;
163 void recordSurroundingText(const std::string
& before_text
,
164 const std::string
& after_text
) {
165 before_text_
= before_text
;
166 after_text_
= after_text
;
169 void recordIcingSelectionAvailable(const std::string
& encoding
,
170 const base::string16
& surrounding_text
,
178 std::string search_term_
;
179 std::string display_text_
;
180 std::string alternate_term_
;
181 bool prevent_preload_
;
184 std::string before_text_
;
185 std::string after_text_
;
187 base::MessageLoopForIO io_message_loop_
;
188 net::TestURLFetcherFactory test_factory_
;
189 net::TestURLFetcher
* fetcher_
;
190 scoped_ptr
<TemplateURLService
> template_url_service_
;
191 scoped_refptr
<net::TestURLRequestContextGetter
> request_context_
;
193 // Will be owned by the delegate.
194 ContextualSearchContext
* test_context_
;
196 DISALLOW_COPY_AND_ASSIGN(ContextualSearchDelegateTest
);
199 TEST_F(ContextualSearchDelegateTest
, NormalFetchWithXssiEscape
) {
200 CreateDefaultSearchContextAndRequestSearchTerm();
201 fetcher()->set_response_code(200);
202 fetcher()->SetResponseString(
204 "{\"mid\":\"/m/02mjmr\", \"search_term\":\"obama\","
205 "\"info_text\":\"44th U.S. President\","
206 "\"display_text\":\"Barack Obama\", \"mentions\":[0,15],"
207 "\"selected_text\":\"obama\", \"resolved_term\":\"barack obama\"}");
208 fetcher()->delegate()->OnURLFetchComplete(fetcher());
210 EXPECT_FALSE(is_invalid());
211 EXPECT_EQ(200, response_code());
212 EXPECT_EQ("obama", search_term());
213 EXPECT_EQ("Barack Obama", display_text());
214 EXPECT_FALSE(do_prevent_preload());
215 EXPECT_TRUE(DoesRequestContainOurSpecificBasePage());
218 TEST_F(ContextualSearchDelegateTest
, NormalFetchWithoutXssiEscape
) {
219 CreateDefaultSearchContextAndRequestSearchTerm();
220 fetcher()->set_response_code(200);
221 fetcher()->SetResponseString(
222 "{\"mid\":\"/m/02mjmr\", \"search_term\":\"obama\","
223 "\"info_text\":\"44th U.S. President\","
224 "\"display_text\":\"Barack Obama\", \"mentions\":[0,15],"
225 "\"selected_text\":\"obama\", \"resolved_term\":\"barack obama\"}");
226 fetcher()->delegate()->OnURLFetchComplete(fetcher());
228 EXPECT_FALSE(is_invalid());
229 EXPECT_EQ(200, response_code());
230 EXPECT_EQ("obama", search_term());
231 EXPECT_EQ("Barack Obama", display_text());
232 EXPECT_FALSE(do_prevent_preload());
233 EXPECT_TRUE(DoesRequestContainOurSpecificBasePage());
236 TEST_F(ContextualSearchDelegateTest
, ResponseWithNoDisplayText
) {
237 CreateDefaultSearchContextAndRequestSearchTerm();
238 fetcher()->set_response_code(200);
239 fetcher()->SetResponseString(
240 "{\"mid\":\"/m/02mjmr\",\"search_term\":\"obama\","
241 "\"mentions\":[0,15]}");
242 fetcher()->delegate()->OnURLFetchComplete(fetcher());
244 EXPECT_FALSE(is_invalid());
245 EXPECT_EQ(200, response_code());
246 EXPECT_EQ("obama", search_term());
247 EXPECT_EQ("obama", display_text());
248 EXPECT_FALSE(do_prevent_preload());
249 EXPECT_TRUE(DoesRequestContainOurSpecificBasePage());
252 TEST_F(ContextualSearchDelegateTest
, ResponseWithPreventPreload
) {
253 CreateDefaultSearchContextAndRequestSearchTerm();
254 fetcher()->set_response_code(200);
255 fetcher()->SetResponseString(
256 "{\"mid\":\"/m/02mjmr\",\"search_term\":\"obama\","
257 "\"mentions\":[0,15],\"prevent_preload\":\"1\"}");
258 fetcher()->delegate()->OnURLFetchComplete(fetcher());
260 EXPECT_FALSE(is_invalid());
261 EXPECT_EQ(200, response_code());
262 EXPECT_EQ("obama", search_term());
263 EXPECT_EQ("obama", display_text());
264 EXPECT_TRUE(do_prevent_preload());
265 EXPECT_TRUE(DoesRequestContainOurSpecificBasePage());
268 TEST_F(ContextualSearchDelegateTest
, NonJsonResponse
) {
269 CreateDefaultSearchContextAndRequestSearchTerm();
270 fetcher()->set_response_code(200);
271 fetcher()->SetResponseString("Non-JSON Response");
272 fetcher()->delegate()->OnURLFetchComplete(fetcher());
274 EXPECT_FALSE(is_invalid());
275 EXPECT_EQ(200, response_code());
276 EXPECT_EQ("", search_term());
277 EXPECT_EQ("", display_text());
278 EXPECT_FALSE(do_prevent_preload());
279 EXPECT_TRUE(DoesRequestContainOurSpecificBasePage());
282 TEST_F(ContextualSearchDelegateTest
, InvalidResponse
) {
283 CreateDefaultSearchContextAndRequestSearchTerm();
284 fetcher()->set_response_code(net::URLFetcher::RESPONSE_CODE_INVALID
);
285 fetcher()->delegate()->OnURLFetchComplete(fetcher());
287 EXPECT_FALSE(do_prevent_preload());
288 EXPECT_TRUE(is_invalid());
291 TEST_F(ContextualSearchDelegateTest
, ExpandSelectionToEnd
) {
292 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
293 std::string selected_text
= "Barack";
294 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
, 0, 6);
295 SetResponseStringAndFetch(selected_text
, "0", "12");
297 EXPECT_EQ(0, start_adjust());
298 EXPECT_EQ(6, end_adjust());
301 TEST_F(ContextualSearchDelegateTest
, ExpandSelectionToStart
) {
302 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
303 std::string selected_text
= "Obama";
304 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
, 7, 12);
305 SetResponseStringAndFetch(selected_text
, "0", "12");
307 EXPECT_EQ(-7, start_adjust());
308 EXPECT_EQ(0, end_adjust());
311 TEST_F(ContextualSearchDelegateTest
, ExpandSelectionBothDirections
) {
312 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
313 std::string selected_text
= "Ob";
314 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
, 7, 9);
315 SetResponseStringAndFetch(selected_text
, "0", "12");
317 EXPECT_EQ(-7, start_adjust());
318 EXPECT_EQ(3, end_adjust());
321 TEST_F(ContextualSearchDelegateTest
, ExpandSelectionInvalidRange
) {
322 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
323 std::string selected_text
= "Ob";
324 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
, 7, 9);
325 SetResponseStringAndFetch(selected_text
, "0", "200");
327 EXPECT_EQ(0, start_adjust());
328 EXPECT_EQ(0, end_adjust());
331 TEST_F(ContextualSearchDelegateTest
, ExpandSelectionInvalidDistantStart
) {
332 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
333 std::string selected_text
= "Ob";
334 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
,
335 0xffffffff, 0xffffffff - 2);
336 SetResponseStringAndFetch(selected_text
, "0", "12");
338 EXPECT_EQ(0, start_adjust());
339 EXPECT_EQ(0, end_adjust());
342 TEST_F(ContextualSearchDelegateTest
, ExpandSelectionInvalidNoOverlap
) {
343 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
344 std::string selected_text
= "Ob";
345 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
, 0, 12);
346 SetResponseStringAndFetch(selected_text
, "12", "14");
348 EXPECT_EQ(0, start_adjust());
349 EXPECT_EQ(0, end_adjust());
352 TEST_F(ContextualSearchDelegateTest
, ExpandSelectionInvalidDistantEndAndRange
) {
353 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
354 std::string selected_text
= "Ob";
355 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
,
356 0xffffffff, 0xffffffff - 2);
357 SetResponseStringAndFetch(selected_text
, "0", "268435455");
359 EXPECT_EQ(0, start_adjust());
360 EXPECT_EQ(0, end_adjust());
363 TEST_F(ContextualSearchDelegateTest
, ExpandSelectionLargeNumbers
) {
364 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
365 std::string selected_text
= "Ob";
366 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
,
367 268435450, 268435455);
368 SetResponseStringAndFetch(selected_text
, "268435440", "268435455");
370 EXPECT_EQ(-10, start_adjust());
371 EXPECT_EQ(0, end_adjust());
374 TEST_F(ContextualSearchDelegateTest
, ContractSelectionValid
) {
375 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
376 std::string selected_text
= "Barack Obama just";
377 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
, 0, 17);
378 SetResponseStringAndFetch(selected_text
, "0", "12");
380 EXPECT_EQ(0, start_adjust());
381 EXPECT_EQ(-5, end_adjust());
384 TEST_F(ContextualSearchDelegateTest
, ContractSelectionInvalid
) {
385 base::string16 surrounding
= base::UTF8ToUTF16("Barack Obama just spoke.");
386 std::string selected_text
= "Barack Obama just";
387 CreateSearchContextAndRequestSearchTerm(selected_text
, surrounding
, 0, 17);
388 SetResponseStringAndFetch(selected_text
, "5", "5");
390 EXPECT_EQ(0, start_adjust());
391 EXPECT_EQ(0, end_adjust());
394 TEST_F(ContextualSearchDelegateTest
, SurroundingTextHighMaximum
) {
395 base::string16 surrounding
= base::ASCIIToUTF16("aa bb Bogus dd ee");
396 SetSurroundingContext(surrounding
, 6, 11);
397 delegate_
->SendSurroundingText(30); // High maximum # of surrounding chars.
398 EXPECT_EQ("aa bb", before_text());
399 EXPECT_EQ("dd ee", after_text());
402 TEST_F(ContextualSearchDelegateTest
, SurroundingTextLowMaximum
) {
403 base::string16 surrounding
= base::ASCIIToUTF16("aa bb Bogus dd ee");
404 SetSurroundingContext(surrounding
, 6, 11);
405 delegate_
->SendSurroundingText(3); // Low maximum # of surrounding chars.
406 // Whitespaces are trimmed.
407 EXPECT_EQ("bb", before_text());
408 EXPECT_EQ("dd", after_text());
411 TEST_F(ContextualSearchDelegateTest
, SurroundingTextNoBeforeText
) {
412 base::string16 surrounding
= base::ASCIIToUTF16("Bogus ee ff gg");
413 SetSurroundingContext(surrounding
, 0, 5);
414 delegate_
->SendSurroundingText(5);
415 EXPECT_EQ("", before_text());
416 EXPECT_EQ("ee f", after_text());
419 TEST_F(ContextualSearchDelegateTest
, ExtractMentionsStartEnd
) {
420 ListValue mentions_list
;
421 mentions_list
.AppendInteger(1);
422 mentions_list
.AppendInteger(2);
425 delegate_
->ExtractMentionsStartEnd(mentions_list
, &start
, &end
);
430 TEST_F(ContextualSearchDelegateTest
, SurroundingTextForIcing
) {
431 base::string16 sample
= base::ASCIIToUTF16("this is Barack Obama in office.");
432 int limit_each_side
= 3;
435 base::string16 result
=
436 delegate_
->SurroundingTextForIcing(sample
, limit_each_side
, &start
, &end
);
437 EXPECT_EQ(static_cast<size_t>(3), start
);
438 EXPECT_EQ(static_cast<size_t>(15), end
);
439 EXPECT_EQ(base::ASCIIToUTF16("is Barack Obama in"), result
);
442 TEST_F(ContextualSearchDelegateTest
, SurroundingTextForIcingNegativeLimit
) {
443 base::string16 sample
= base::ASCIIToUTF16("this is Barack Obama in office.");
444 int limit_each_side
= -2;
447 base::string16 result
=
448 delegate_
->SurroundingTextForIcing(sample
, limit_each_side
, &start
, &end
);
449 EXPECT_EQ(static_cast<size_t>(0), start
);
450 EXPECT_EQ(static_cast<size_t>(12), end
);
451 EXPECT_EQ(base::ASCIIToUTF16("Barack Obama"), result
);
454 TEST_F(ContextualSearchDelegateTest
, DecodeSearchTermsFromJsonResponse
) {
455 std::string json_with_escape
=
457 "{\"mid\":\"/m/02mjmr\", \"search_term\":\"obama\","
458 "\"info_text\":\"44th U.S. President\","
459 "\"display_text\":\"Barack Obama\", \"mentions\":[0,15],"
460 "\"selected_text\":\"obama\", \"resolved_term\":\"barack obama\"}";
461 std::string search_term
;
462 std::string display_text
;
463 std::string alternate_term
;
464 std::string prevent_preload
;
467 delegate_
->DecodeSearchTermsFromJsonResponse(json_with_escape
, &search_term
,
468 &display_text
, &alternate_term
,
469 &prevent_preload
, &mention_start
,
471 EXPECT_EQ("obama", search_term
);
472 EXPECT_EQ("Barack Obama", display_text
);
473 EXPECT_EQ("barack obama", alternate_term
);
474 EXPECT_EQ("", prevent_preload
);