Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/android/contextualsearch/contextual_search_delegate.h" | 5 #include "chrome/browser/android/contextualsearch/contextual_search_delegate.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <algorithm> | |
| 9 #include <memory> | 10 #include <memory> |
| 11 #include <string> | |
| 10 | 12 |
| 11 #include "base/base64.h" | 13 #include "base/base64.h" |
| 12 #include "base/macros.h" | 14 #include "base/macros.h" |
| 13 #include "base/memory/ptr_util.h" | 15 #include "base/memory/ptr_util.h" |
| 14 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 16 #include "base/values.h" | 18 #include "base/values.h" |
| 17 #include "chrome/browser/android/contextualsearch/contextual_search_context.h" | 19 #include "chrome/browser/android/contextualsearch/contextual_search_context.h" |
| 20 #include "chrome/browser/android/contextualsearch/contextual_search_field_trial. h" | |
| 18 #include "chrome/browser/android/contextualsearch/resolved_search_term.h" | 21 #include "chrome/browser/android/contextualsearch/resolved_search_term.h" |
| 19 #include "chrome/browser/android/proto/client_discourse_context.pb.h" | 22 #include "chrome/browser/android/proto/client_discourse_context.pb.h" |
| 20 #include "components/search_engines/template_url_service.h" | 23 #include "components/search_engines/template_url_service.h" |
| 21 #include "net/base/escape.h" | 24 #include "net/base/escape.h" |
| 22 #include "net/url_request/test_url_fetcher_factory.h" | 25 #include "net/url_request/test_url_fetcher_factory.h" |
| 23 #include "net/url_request/url_request_test_util.h" | 26 #include "net/url_request/url_request_test_util.h" |
| 24 #include "testing/gtest/include/gtest/gtest.h" | 27 #include "testing/gtest/include/gtest/gtest.h" |
| 25 | 28 |
| 26 using base::ListValue; | 29 using base::ListValue; |
| 27 | 30 |
| 28 namespace { | 31 namespace { |
| 29 | 32 |
| 30 const char kSomeSpecificBasePage[] = "http://some.specific.host.name.com/"; | 33 const char kSomeSpecificBasePage[] = "http://some.specific.host.name.com/"; |
| 31 const char kDiscourseContextHeaderName[] = "X-Additional-Discourse-Context"; | 34 const char kDiscourseContextHeaderName[] = "X-Additional-Discourse-Context"; |
| 32 | 35 |
| 33 } // namespace | 36 } // namespace |
| 34 | 37 |
| 35 class ContextualSearchDelegateTest : public testing::Test { | 38 class ContextualSearchDelegateTest : public testing::Test { |
| 36 public: | 39 public: |
| 37 ContextualSearchDelegateTest() {} | 40 ContextualSearchDelegateTest() {} |
| 38 ~ContextualSearchDelegateTest() override {} | 41 ~ContextualSearchDelegateTest() override {} |
| 39 | 42 |
| 43 // Inner class that enables Now on Tap Bar integration. | |
| 44 class ContextualSearchFieldTrialStubbed : public ContextualSearchFieldTrial { | |
| 45 public: | |
| 46 bool IsNowOnTapBarIntegrationEnabled() override { return true; } | |
|
Theresa
2016/09/20 19:51:40
I don't think this is necessary. There should be a
Donn Denman
2016/09/20 21:16:37
Oh, I feel bad that I didn't figure this out on my
| |
| 47 }; | |
| 48 | |
| 40 protected: | 49 protected: |
| 41 void SetUp() override { | 50 void SetUp() override { |
| 42 request_context_ = | 51 request_context_ = |
| 43 new net::TestURLRequestContextGetter(io_message_loop_.task_runner()); | 52 new net::TestURLRequestContextGetter(io_message_loop_.task_runner()); |
| 44 template_url_service_.reset(CreateTemplateURLService()); | 53 template_url_service_.reset(CreateTemplateURLService()); |
| 45 delegate_.reset(new ContextualSearchDelegate( | 54 delegate_.reset(new ContextualSearchDelegate( |
| 46 request_context_.get(), template_url_service_.get(), | 55 request_context_.get(), template_url_service_.get(), |
| 47 base::Bind( | 56 base::Bind( |
| 48 &ContextualSearchDelegateTest::recordSearchTermResolutionResponse, | 57 &ContextualSearchDelegateTest::recordSearchTermResolutionResponse, |
| 49 base::Unretained(this)), | 58 base::Unretained(this)), |
| 50 base::Bind(&ContextualSearchDelegateTest::recordSurroundingText, | 59 base::Bind(&ContextualSearchDelegateTest::recordSurroundingText, |
| 51 base::Unretained(this)), | 60 base::Unretained(this)), |
| 52 base::Bind(&ContextualSearchDelegateTest::recordIcingSelectionAvailable, | 61 base::Bind(&ContextualSearchDelegateTest::recordIcingSelectionAvailable, |
| 53 base::Unretained(this)))); | 62 base::Unretained(this)), |
| 63 std::unique_ptr<ContextualSearchFieldTrial>( | |
| 64 new ContextualSearchFieldTrialStubbed()))); | |
| 54 } | 65 } |
| 55 | 66 |
| 56 void TearDown() override { | 67 void TearDown() override { |
| 57 fetcher_ = NULL; | 68 fetcher_ = NULL; |
| 58 is_invalid_ = true; | 69 is_invalid_ = true; |
| 59 response_code_ = -1; | 70 response_code_ = -1; |
| 60 search_term_ = "invalid"; | 71 search_term_ = "invalid"; |
| 61 mid_ = ""; | 72 mid_ = ""; |
| 62 display_text_ = "unknown"; | 73 display_text_ = "unknown"; |
| 63 context_language_ = ""; | 74 context_language_ = ""; |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 94 test_context_->start_offset = start_offset; | 105 test_context_->start_offset = start_offset; |
| 95 test_context_->end_offset = end_offset; | 106 test_context_->end_offset = end_offset; |
| 96 test_context_->surrounding_text = surrounding_text; | 107 test_context_->surrounding_text = surrounding_text; |
| 97 delegate_->ContinueSearchTermResolutionRequest(); | 108 delegate_->ContinueSearchTermResolutionRequest(); |
| 98 fetcher_ = test_factory_.GetFetcherByID( | 109 fetcher_ = test_factory_.GetFetcherByID( |
| 99 ContextualSearchDelegate::kContextualSearchURLFetcherID); | 110 ContextualSearchDelegate::kContextualSearchURLFetcherID); |
| 100 ASSERT_TRUE(fetcher_); | 111 ASSERT_TRUE(fetcher_); |
| 101 ASSERT_TRUE(fetcher()); | 112 ASSERT_TRUE(fetcher()); |
| 102 } | 113 } |
| 103 | 114 |
| 115 // Allows using the vertical bar "|" as a quote character, which makes | |
| 116 // test cases more readable versus the escaped double quote that is otherwise | |
| 117 // needed for JSON literals. | |
| 118 std::string escapeBarQuoted(std::string bar_quoted) { | |
| 119 std::replace(bar_quoted.begin(), bar_quoted.end(), '|', '\"'); | |
| 120 return bar_quoted; | |
| 121 } | |
| 122 | |
| 123 void CreateDefaultSearchWithContextualCardsData( | |
| 124 const std::string contextual_cards_data) { | |
| 125 CreateDefaultSearchContextAndRequestSearchTerm(); | |
| 126 fetcher()->set_response_code(200); | |
| 127 std::string response = | |
| 128 escapeBarQuoted("{|search_term|:|obama|" + contextual_cards_data + "}"); | |
| 129 fetcher()->SetResponseString(response); | |
| 130 fetcher()->delegate()->OnURLFetchComplete(fetcher()); | |
| 131 | |
| 132 EXPECT_FALSE(is_invalid()); | |
| 133 EXPECT_EQ(200, response_code()); | |
| 134 EXPECT_EQ("obama", search_term()); | |
| 135 } | |
| 136 | |
| 137 void CreateDefaultSearchWithContextualCardsValue( | |
| 138 const std::string contextual_cards_value) { | |
| 139 CreateDefaultSearchWithContextualCardsData(", |contextual_cards|:" + | |
| 140 contextual_cards_value); | |
| 141 } | |
| 142 | |
| 104 void SetResponseStringAndFetch(const std::string& selected_text, | 143 void SetResponseStringAndFetch(const std::string& selected_text, |
| 105 const std::string& mentions_start, | 144 const std::string& mentions_start, |
| 106 const std::string& mentions_end) { | 145 const std::string& mentions_end) { |
| 107 fetcher()->set_response_code(200); | 146 fetcher()->set_response_code(200); |
| 108 fetcher()->SetResponseString( | 147 fetcher()->SetResponseString( |
| 109 ")]}'\n" | 148 ")]}'\n" |
| 110 "{\"mid\":\"/m/02mjmr\", \"search_term\":\"obama\"," | 149 "{\"mid\":\"/m/02mjmr\", \"search_term\":\"obama\"," |
| 111 "\"info_text\":\"44th U.S. President\"," | 150 "\"info_text\":\"44th U.S. President\"," |
| 112 "\"display_text\":\"Barack Obama\"," | 151 "\"display_text\":\"Barack Obama\"," |
| 113 "\"mentions\":[" + mentions_start + ","+ mentions_end + "]," | 152 "\"mentions\":[" + mentions_start + ","+ mentions_end + "]," |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 return result; | 204 return result; |
| 166 } | 205 } |
| 167 | 206 |
| 168 net::TestURLFetcher* fetcher() { return fetcher_; } | 207 net::TestURLFetcher* fetcher() { return fetcher_; } |
| 169 bool is_invalid() { return is_invalid_; } | 208 bool is_invalid() { return is_invalid_; } |
| 170 int response_code() { return response_code_; } | 209 int response_code() { return response_code_; } |
| 171 std::string search_term() { return search_term_; } | 210 std::string search_term() { return search_term_; } |
| 172 std::string display_text() { return display_text_; } | 211 std::string display_text() { return display_text_; } |
| 173 std::string alternate_term() { return alternate_term_; } | 212 std::string alternate_term() { return alternate_term_; } |
| 174 std::string mid() { return mid_; } | 213 std::string mid() { return mid_; } |
| 214 std::string caption() { return caption_; } | |
| 215 std::string thumbnail_url() { return thumbnail_url_; } | |
| 175 bool do_prevent_preload() { return prevent_preload_; } | 216 bool do_prevent_preload() { return prevent_preload_; } |
| 176 std::string after_text() { return after_text_; } | 217 std::string after_text() { return after_text_; } |
| 177 int start_adjust() { return start_adjust_; } | 218 int start_adjust() { return start_adjust_; } |
| 178 int end_adjust() { return end_adjust_; } | 219 int end_adjust() { return end_adjust_; } |
| 179 std::string context_language() { return context_language_; } | 220 std::string context_language() { return context_language_; } |
| 180 | 221 |
| 181 // The delegate under test. | 222 // The delegate under test. |
| 182 std::unique_ptr<ContextualSearchDelegate> delegate_; | 223 std::unique_ptr<ContextualSearchDelegate> delegate_; |
| 183 | 224 |
| 184 private: | 225 private: |
| 185 void recordSearchTermResolutionResponse( | 226 void recordSearchTermResolutionResponse( |
| 186 const ResolvedSearchTerm& resolved_search_term) { | 227 const ResolvedSearchTerm& resolved_search_term) { |
| 187 is_invalid_ = resolved_search_term.is_invalid; | 228 is_invalid_ = resolved_search_term.is_invalid; |
| 188 response_code_ = resolved_search_term.response_code; | 229 response_code_ = resolved_search_term.response_code; |
| 189 search_term_ = resolved_search_term.search_term; | 230 search_term_ = resolved_search_term.search_term; |
| 190 display_text_ = resolved_search_term.display_text; | 231 display_text_ = resolved_search_term.display_text; |
| 191 alternate_term_ = resolved_search_term.alternate_term; | 232 alternate_term_ = resolved_search_term.alternate_term; |
| 192 mid_ = resolved_search_term.mid; | 233 mid_ = resolved_search_term.mid; |
| 234 thumbnail_url_ = resolved_search_term.thumbnail_url; | |
| 235 caption_ = resolved_search_term.caption; | |
| 193 prevent_preload_ = resolved_search_term.prevent_preload; | 236 prevent_preload_ = resolved_search_term.prevent_preload; |
| 194 start_adjust_ = resolved_search_term.selection_start_adjust; | 237 start_adjust_ = resolved_search_term.selection_start_adjust; |
| 195 end_adjust_ = resolved_search_term.selection_end_adjust; | 238 end_adjust_ = resolved_search_term.selection_end_adjust; |
| 196 context_language_ = resolved_search_term.context_language; | 239 context_language_ = resolved_search_term.context_language; |
| 197 } | 240 } |
| 198 | 241 |
| 199 void recordSurroundingText(const std::string& after_text) { | 242 void recordSurroundingText(const std::string& after_text) { |
| 200 after_text_ = after_text; | 243 after_text_ = after_text; |
| 201 } | 244 } |
| 202 | 245 |
| 203 void recordIcingSelectionAvailable(const std::string& encoding, | 246 void recordIcingSelectionAvailable(const std::string& encoding, |
| 204 const base::string16& surrounding_text, | 247 const base::string16& surrounding_text, |
| 205 size_t start_offset, | 248 size_t start_offset, |
| 206 size_t end_offset) { | 249 size_t end_offset) { |
| 207 // unused. | 250 // unused. |
| 208 } | 251 } |
| 209 | 252 |
| 210 bool is_invalid_; | 253 bool is_invalid_; |
| 211 int response_code_; | 254 int response_code_; |
| 212 std::string search_term_; | 255 std::string search_term_; |
| 213 std::string display_text_; | 256 std::string display_text_; |
| 214 std::string alternate_term_; | 257 std::string alternate_term_; |
| 215 std::string mid_; | 258 std::string mid_; |
| 259 std::string thumbnail_url_; | |
| 260 std::string caption_; | |
| 216 bool prevent_preload_; | 261 bool prevent_preload_; |
| 217 int start_adjust_; | 262 int start_adjust_; |
| 218 int end_adjust_; | 263 int end_adjust_; |
| 219 std::string after_text_; | 264 std::string after_text_; |
| 220 std::string context_language_; | 265 std::string context_language_; |
| 221 | 266 |
| 222 base::MessageLoopForIO io_message_loop_; | 267 base::MessageLoopForIO io_message_loop_; |
| 223 net::TestURLFetcherFactory test_factory_; | 268 net::TestURLFetcherFactory test_factory_; |
| 224 net::TestURLFetcher* fetcher_; | 269 net::TestURLFetcher* fetcher_; |
| 225 std::unique_ptr<TemplateURLService> template_url_service_; | 270 std::unique_ptr<TemplateURLService> template_url_service_; |
| (...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 536 EXPECT_EQ("obama", display_text()); | 581 EXPECT_EQ("obama", display_text()); |
| 537 EXPECT_EQ("/m/02mjmr", mid()); | 582 EXPECT_EQ("/m/02mjmr", mid()); |
| 538 EXPECT_TRUE(do_prevent_preload()); | 583 EXPECT_TRUE(do_prevent_preload()); |
| 539 EXPECT_EQ("de", context_language()); | 584 EXPECT_EQ("de", context_language()); |
| 540 } | 585 } |
| 541 | 586 |
| 542 TEST_F(ContextualSearchDelegateTest, HeaderContainsBasePageUrl) { | 587 TEST_F(ContextualSearchDelegateTest, HeaderContainsBasePageUrl) { |
| 543 CreateDefaultSearchContextAndRequestSearchTerm(); | 588 CreateDefaultSearchContextAndRequestSearchTerm(); |
| 544 EXPECT_EQ(kSomeSpecificBasePage, getBasePageUrlFromRequest()); | 589 EXPECT_EQ(kSomeSpecificBasePage, getBasePageUrlFromRequest()); |
| 545 } | 590 } |
| 591 | |
| 592 // Tests a response with Now on Tap data. | |
| 593 TEST_F(ContextualSearchDelegateTest, ContextualCardsResponseSingleCard) { | |
| 594 CreateDefaultSearchContextAndRequestSearchTerm(); | |
| 595 fetcher()->set_response_code(200); | |
| 596 fetcher()->SetResponseString( | |
| 597 "{\"search_term\":\"obama\"," | |
|
Theresa
2016/09/20 19:51:40
Can we use escapeBarQuoted() here too?
Donn Denman
2016/09/20 21:16:37
I'll just remove this one.
| |
| 598 "\"contextual_cards\":{\"cards\":[{\"singleCard\":{ " | |
| 599 "\"subtitle\": \"president\", " | |
| 600 "\"thumbnail\":{\"uri\":\"https://t0.gstatic.com/images?q=tbn:ANd9\"}" | |
| 601 "}}]}}"); | |
| 602 fetcher()->delegate()->OnURLFetchComplete(fetcher()); | |
| 603 | |
| 604 EXPECT_FALSE(is_invalid()); | |
| 605 EXPECT_EQ(200, response_code()); | |
| 606 EXPECT_EQ("obama", search_term()); | |
| 607 EXPECT_EQ("president", caption()); | |
| 608 EXPECT_EQ("https://t0.gstatic.com/images?q=tbn:ANd9", thumbnail_url()); | |
| 609 } | |
| 610 | |
| 611 // Same test using helpers and "|" quoting (for readability). | |
|
Theresa
2016/09/20 19:51:40
Does duplicating the test give us any extra code c
Donn Denman
2016/09/20 21:16:37
Done.
| |
| 612 TEST_F(ContextualSearchDelegateTest, | |
| 613 ContextualCardsResponseSingleCardBarQuotedTest) { | |
| 614 CreateDefaultSearchWithContextualCardsValue( | |
| 615 "{|cards|:[{|singleCard|:{|subtitle|: |president|," | |
| 616 "|thumbnail|:{|uri|:|https://t0.gstatic.com/images?q=tbn:ANd9|}}}]}"); | |
| 617 EXPECT_EQ("president", caption()); | |
| 618 EXPECT_EQ("https://t0.gstatic.com/images?q=tbn:ANd9", thumbnail_url()); | |
| 619 } | |
| 620 | |
| 621 // Missing all Now on Tap data. | |
| 622 TEST_F(ContextualSearchDelegateTest, ContextualCardsResponseWithNoData) { | |
| 623 CreateDefaultSearchWithContextualCardsData(""); | |
| 624 EXPECT_EQ("", caption()); | |
| 625 EXPECT_EQ("", thumbnail_url()); | |
| 626 } | |
| 627 | |
| 628 // Missing subtitle (for caption). | |
| 629 TEST_F(ContextualSearchDelegateTest, | |
| 630 ContextualCardsResponseWithMissingCaption) { | |
| 631 CreateDefaultSearchWithContextualCardsValue( | |
| 632 "{|cards|:[{|singleCard|:{|stubtitlemisspelled|: |president|," | |
| 633 "|thumbnail|:{|uri|:|https://t0.gstatic.com/images?q=tbn:ANd9|}}}]}"); | |
| 634 EXPECT_EQ("", caption()); | |
| 635 EXPECT_EQ("https://t0.gstatic.com/images?q=tbn:ANd9", thumbnail_url()); | |
| 636 } | |
| 637 | |
| 638 // Missing the Thumbnail URI. | |
| 639 TEST_F(ContextualSearchDelegateTest, | |
| 640 ContextualCardsResponseWithMissingThumbnailUri) { | |
| 641 CreateDefaultSearchWithContextualCardsValue( | |
| 642 "{|cards|:[{|singleCard|:{|subtitle|: |president|," | |
| 643 "|thumbnail|:{}}}]}"); | |
| 644 EXPECT_EQ("president", caption()); | |
| 645 EXPECT_EQ("", thumbnail_url()); | |
| 646 } | |
| 647 | |
| 648 // Missing the whole Thumbnail. | |
| 649 TEST_F(ContextualSearchDelegateTest, | |
| 650 ContextualCardsResponseWithMissingThumbnail) { | |
| 651 CreateDefaultSearchWithContextualCardsValue( | |
| 652 "{|cards|:[{|singleCard|:{|subtitle|: |president|," | |
| 653 "|ignored key|:|ignored value|}}]}"); | |
| 654 EXPECT_EQ("president", caption()); | |
| 655 EXPECT_EQ("", thumbnail_url()); | |
| 656 } | |
| 657 | |
| 658 // Empty cards list. | |
| 659 TEST_F(ContextualSearchDelegateTest, | |
| 660 ContextualCardsResponseWithMissingSingleCard) { | |
| 661 CreateDefaultSearchWithContextualCardsValue("{|cards|:[]}"); | |
| 662 EXPECT_EQ("", caption()); | |
| 663 EXPECT_EQ("", thumbnail_url()); | |
| 664 } | |
| 665 | |
| 666 // Tests carouselCard followed by singleCard. | |
| 667 TEST_F(ContextualSearchDelegateTest, | |
| 668 ContextualCardsResponseWithSingleAndCarouselCards) { | |
| 669 CreateDefaultSearchWithContextualCardsValue( | |
| 670 "{|cards|:[{|carouselCard|:{}},{|singleCard|:{|subtitle|: |president|," | |
| 671 "|thumbnail|:{|uri|:|https://t0.gstatic.com/images?q=tbn:ANd9|}}}]}"); | |
| 672 EXPECT_EQ("president", caption()); | |
| 673 EXPECT_EQ("https://t0.gstatic.com/images?q=tbn:ANd9", thumbnail_url()); | |
| 674 } | |
| 675 | |
| 676 // Missing cards. | |
| 677 TEST_F(ContextualSearchDelegateTest, ContextualCardsResponseWithMissingCards) { | |
| 678 CreateDefaultSearchWithContextualCardsValue("{}"); | |
| 679 EXPECT_EQ("", caption()); | |
| 680 EXPECT_EQ("", thumbnail_url()); | |
| 681 } | |
| 682 | |
| 683 // Multiple cards (latter should be ignored). | |
| 684 TEST_F(ContextualSearchDelegateTest, ContextualCardsResponseWithMultipleCards) { | |
| 685 CreateDefaultSearchWithContextualCardsValue( | |
| 686 "{|cards|:[{|singleCard|:{|subtitle|: |president|," | |
| 687 "|thumbnail|:{|uri|:|https://t0.gstatic.com/images?q=tbn:ANd9|}}}," | |
| 688 "{|singleCard|:{|subtitle|:|wrong subtitle|," | |
| 689 "|thumbnail|:{|uri|:|https://t0.gstatic.com/wrongThumbnail|}}}]}"); | |
| 690 EXPECT_EQ("president", caption()); | |
| 691 EXPECT_EQ("https://t0.gstatic.com/images?q=tbn:ANd9", thumbnail_url()); | |
| 692 } | |
| OLD | NEW |