12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <string>
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/basictypes.h"
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/bind.h"
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/command_line.h"
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/memory/scoped_ptr.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/run_loop.h"
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/utf_string_conversions.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/profiles/profile_manager.h"
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/ui/app_list/search/people/people_provider.h"
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/chrome_switches.h"
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/test/base/in_process_browser_test.h"
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/test/embedded_test_server/embedded_test_server.h"
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/test/embedded_test_server/http_request.h"
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/test/embedded_test_server/http_response.h"
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::BrowserThread;
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using net::test_server::BasicHttpResponse;
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using net::test_server::HttpRequest;
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using net::test_server::HttpResponse;
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using net::test_server::EmbeddedTestServer;
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace app_list {
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace test {
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Mock results.
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kOneResult[] = "{"
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    "\"items\":["
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "{"
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        "\"person\" : {"
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "\"id\": \"1\","
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"metadata\" : {"
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"ownerId\": \"1\""
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "},"
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"names\" : [{"
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"displayName\": \"first person\""
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "}],"
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"emails\" : [{"
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"value\": \"first@person.com\""
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "}],"
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"images\" : [{"
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"url\": \"http://host/icon\""
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "}],"
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"sortKeys\" : {"
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "\"interactionRank\": \"0.98\""
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "}"
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        "}"
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "}"
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    "]}";
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kThreeValidResults[] = "{"
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    "\"items\":["
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      "{"
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        "\"person\" : {"
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"id\": \"1\","
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"metadata\" : {"
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "\"ownerId\": \"1\""
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "},"
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"names\" : [{"
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"displayName\": \"first person\""
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "}],"
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"emails\" : [{"
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"value\": \"first@person.com\""
71424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)          "}],"
72424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)          "\"images\" : [{"
73424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)            "\"url\": \"http://host/icon\""
74424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)          "}],"
75424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)          "\"sortKeys\" : {"
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"interactionRank\": \"0.98\""
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "}"
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        "}"
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      "},"
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "{"
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        "\"person\" : {"
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "\"id\": \"2\","
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "\"metadata\" : {"
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"ownerId\": \"37\""
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "},"
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"names\" : [{"
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"displayName\": \"second person\""
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "}],"
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"emails\" : [{"
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"value\": \"second@person.com\""
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "}],"
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"images\" : [{"
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            "\"url\": \"http://host/icon\""
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "}],"
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          "\"sortKeys\" : {"
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            "\"interactionRank\": \"0.84\""
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          "}"
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@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@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@person.com\""
151          "}],"
152          // Images field is missing on purpose.
153          "\"sortKeys\" : {"
154            "\"interactionRank\": \"0.98\""
155          "}"
156        "}"
157      "}"
158    "]}";
159
160}  // namespace
161
162class 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 TearDownOnMainThread() 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
249IN_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
267IN_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
288IN_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