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 <string> 6 7 #include "base/basictypes.h" 8 #include "base/bind.h" 9 #include "base/command_line.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/run_loop.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "chrome/browser/profiles/profile_manager.h" 14 #include "chrome/browser/ui/app_list/search/chrome_search_result.h" 15 #include "chrome/browser/ui/app_list/search/people/people_provider.h" 16 #include "chrome/common/chrome_switches.h" 17 #include "chrome/test/base/in_process_browser_test.h" 18 #include "content/public/browser/browser_thread.h" 19 #include "net/test/embedded_test_server/embedded_test_server.h" 20 #include "net/test/embedded_test_server/http_request.h" 21 #include "net/test/embedded_test_server/http_response.h" 22 23 using content::BrowserThread; 24 using net::test_server::BasicHttpResponse; 25 using net::test_server::HttpRequest; 26 using net::test_server::HttpResponse; 27 using net::test_server::EmbeddedTestServer; 28 29 namespace app_list { 30 namespace test { 31 namespace { 32 33 // Mock results. 34 const char kOneResult[] = "{" 35 "\"items\":[" 36 "{" 37 "\"person\" : {" 38 "\"id\": \"1\"," 39 "\"metadata\" : {" 40 "\"ownerId\": \"1\"" 41 "}," 42 "\"names\" : [{" 43 "\"displayName\": \"first person\"" 44 "}]," 45 "\"emails\" : [{" 46 "\"value\": \"first (at) person.com\"" 47 "}]," 48 "\"images\" : [{" 49 "\"url\": \"http://host/icon\"" 50 "}]," 51 "\"sortKeys\" : {" 52 "\"interactionRank\": \"0.98\"" 53 "}" 54 "}" 55 "}" 56 "]}"; 57 58 const char kThreeValidResults[] = "{" 59 "\"items\":[" 60 "{" 61 "\"person\" : {" 62 "\"id\": \"1\"," 63 "\"metadata\" : {" 64 "\"ownerId\": \"1\"" 65 "}," 66 "\"names\" : [{" 67 "\"displayName\": \"first person\"" 68 "}]," 69 "\"emails\" : [{" 70 "\"value\": \"first (at) person.com\"" 71 "}]," 72 "\"images\" : [{" 73 "\"url\": \"http://host/icon\"" 74 "}]," 75 "\"sortKeys\" : {" 76 "\"interactionRank\": \"0.98\"" 77 "}" 78 "}" 79 "}," 80 "{" 81 "\"person\" : {" 82 "\"id\": \"2\"," 83 "\"metadata\" : {" 84 "\"ownerId\": \"37\"" 85 "}," 86 "\"names\" : [{" 87 "\"displayName\": \"second person\"" 88 "}]," 89 "\"emails\" : [{" 90 "\"value\": \"second (at) person.com\"" 91 "}]," 92 "\"images\" : [{" 93 "\"url\": \"http://host/icon\"" 94 "}]," 95 "\"sortKeys\" : {" 96 "\"interactionRank\": \"0.84\"" 97 "}" 98 "}" 99 "}," 100 "{" 101 "\"person\" : {" 102 "\"id\": \"3\"," 103 "\"metadata\" : {" 104 "\"ownerId\": \"3\"" 105 "}," 106 "\"names\" : [{" 107 "\"displayName\": \"third person\"" 108 "}]," 109 "\"emails\" : [{" 110 "\"value\": \"third (at) person.com\"" 111 "}]," 112 "\"images\" : [{" 113 "\"url\": \"http://host/icon\"" 114 "}]," 115 "\"sortKeys\" : {" 116 "\"interactionRank\": \"0.67\"" 117 "}" 118 "}" 119 "}," 120 "{" 121 "\"person\" : {" 122 "\"id\": \"4\"," 123 "\"metadata\" : {" 124 "\"ownerId\": \"4\"" 125 "}," 126 "\"names\" : [{" 127 "\"displayName\": \"fourth person\"" 128 "}]," 129 "\"emails\" : [{" 130 "\"value\": \"fourth (at) person.com\"" 131 "}]," 132 "\"images\" : [{" 133 "\"url\": \"http://host/icon\"" 134 "}]," 135 "\"sortKeys\" : {" 136 "\"interactionRank\": \"0.0\"" 137 "}" 138 "}" 139 "}," 140 "{" 141 "\"person\" : {" 142 "\"id\": \"5\"," 143 "\"metadata\" : {" 144 "\"ownerId\": \"5\"" 145 "}," 146 "\"names\" : [{" 147 "\"displayName\": \"fifth person\"" 148 "}]," 149 "\"emails\" : [{" 150 "\"value\": \"fifth (at) person.com\"" 151 "}]," 152 // Images field is missing on purpose. 153 "\"sortKeys\" : {" 154 "\"interactionRank\": \"0.98\"" 155 "}" 156 "}" 157 "}" 158 "]}"; 159 160 } // namespace 161 162 class PeopleProviderTest : public InProcessBrowserTest { 163 public: 164 PeopleProviderTest() {} 165 virtual ~PeopleProviderTest() {} 166 167 // InProcessBrowserTest overrides: 168 virtual void SetUpOnMainThread() OVERRIDE { 169 test_server_.reset(new EmbeddedTestServer); 170 171 ASSERT_TRUE(test_server_->InitializeAndWaitUntilReady()); 172 test_server_->RegisterRequestHandler( 173 base::Bind(&PeopleProviderTest::HandleRequest, 174 base::Unretained(this))); 175 176 people_provider_.reset(new PeopleProvider( 177 ProfileManager::GetActiveUserProfile())); 178 179 people_provider_->SetupForTest( 180 base::Bind(&PeopleProviderTest::OnSearchResultsFetched, 181 base::Unretained(this)), 182 test_server_->base_url()); 183 people_provider_->set_use_throttling(false); 184 } 185 186 virtual void CleanUpOnMainThread() OVERRIDE { 187 EXPECT_TRUE(test_server_->ShutdownAndWaitUntilComplete()); 188 test_server_.reset(); 189 } 190 191 std::string RunQuery(const std::string& query, 192 const std::string& mock_server_response) { 193 people_provider_->Start(base::UTF8ToUTF16(query)); 194 195 if (people_provider_->people_search_ && !mock_server_response.empty()) { 196 mock_server_response_ = mock_server_response; 197 198 DCHECK(!run_loop_); 199 run_loop_.reset(new base::RunLoop); 200 run_loop_->Run(); 201 run_loop_.reset(); 202 203 mock_server_response_.clear(); 204 } 205 206 people_provider_->Stop(); 207 return GetResults(); 208 } 209 210 std::string GetResults() const { 211 std::string results; 212 for (SearchProvider::Results::const_iterator it = 213 people_provider_->results().begin(); 214 it != people_provider_->results().end(); 215 ++it) { 216 if (!results.empty()) 217 results += ','; 218 results += base::UTF16ToUTF8((*it)->title()); 219 } 220 return results; 221 } 222 223 PeopleProvider* people_provider() { return people_provider_.get(); } 224 225 private: 226 scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) { 227 scoped_ptr<BasicHttpResponse> response(new BasicHttpResponse); 228 response->set_code(net::HTTP_OK); 229 response->set_content(mock_server_response_); 230 231 return response.PassAs<HttpResponse>(); 232 } 233 234 void OnSearchResultsFetched() { 235 if (run_loop_) 236 run_loop_->Quit(); 237 } 238 239 scoped_ptr<EmbeddedTestServer> test_server_; 240 scoped_ptr<base::RunLoop> run_loop_; 241 242 std::string mock_server_response_; 243 244 scoped_ptr<PeopleProvider> people_provider_; 245 246 DISALLOW_COPY_AND_ASSIGN(PeopleProviderTest); 247 }; 248 249 IN_PROC_BROWSER_TEST_F(PeopleProviderTest, Basic) { 250 struct { 251 const char* query; 252 const char* mock_server_response; 253 const char* expected_results_content; 254 } kTestCases[] = { 255 {"first", kOneResult, "first person" }, 256 {"person", kThreeValidResults, "first person,second person,third person" }, 257 }; 258 259 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) { 260 EXPECT_EQ(kTestCases[i].expected_results_content, 261 RunQuery(kTestCases[i].query, 262 kTestCases[i].mock_server_response)) 263 << "Case " << i << ": q=" << kTestCases[i].query; 264 } 265 } 266 267 IN_PROC_BROWSER_TEST_F(PeopleProviderTest, NoSearchForSensitiveData) { 268 // None of the following input strings should be accepted because they may 269 // contain private data. 270 const char* inputs[] = { 271 // file: scheme is bad. 272 "file://filename", 273 "FILE://filename", 274 // URLs with usernames, ports, queries or refs are bad. 275 "http://username:password@hostname/", 276 "http://www.example.com:1000", 277 "http://foo:1000", 278 "http://hostname/?query=q", 279 "http://hostname/path#ref", 280 // A https URL with path is bad. 281 "https://hostname/path", 282 }; 283 284 for (size_t i = 0; i < arraysize(inputs); ++i) 285 EXPECT_EQ("", RunQuery(inputs[i], kOneResult)); 286 } 287 288 IN_PROC_BROWSER_TEST_F(PeopleProviderTest, NoSearchForShortQueries) { 289 EXPECT_EQ("", RunQuery("f", kOneResult)); 290 EXPECT_EQ("", RunQuery("fi", kOneResult)); 291 EXPECT_EQ("first person", RunQuery("fir", kOneResult)); 292 } 293 294 } // namespace test 295 } // namespace app_list 296