webstore_provider_browsertest.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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/webstore/webstore_provider.h"
16#include "chrome/browser/ui/app_list/search/webstore/webstore_result.h"
17#include "chrome/common/chrome_switches.h"
18#include "chrome/test/base/in_process_browser_test.h"
19#include "content/public/browser/browser_thread.h"
20#include "extensions/common/extension.h"
21#include "net/test/embedded_test_server/embedded_test_server.h"
22#include "net/test/embedded_test_server/http_request.h"
23#include "net/test/embedded_test_server/http_response.h"
24
25using content::BrowserThread;
26using extensions::Manifest;
27using net::test_server::BasicHttpResponse;
28using net::test_server::HttpRequest;
29using net::test_server::HttpResponse;
30using net::test_server::EmbeddedTestServer;
31
32namespace app_list {
33namespace test {
34namespace {
35
36// Mock results.
37const char kOneResult[] = "{"
38    "\"search_url\": \"http://host/search\","
39    "\"results\":["
40      "{"
41        "\"id\": \"app1_id\","
42        "\"localized_name\": \"app1 name\","
43        "\"icon_url\": \"http://host/icon\""
44      "}"
45    "]}";
46
47const char kThreeResults[] = "{"
48    "\"search_url\": \"http://host/search\","
49    "\"results\":["
50      "{"
51        "\"id\": \"app1_id\","
52        "\"localized_name\": \"one\","
53        "\"icon_url\": \"http://host/icon1\","
54        "\"item_type\": \"PLATFORM_APP\""
55      "},"
56      "{"
57        "\"id\": \"app2_id\","
58        "\"localized_name\": \"two\","
59        "\"icon_url\": \"http://host/icon2\","
60        "\"item_type\": \"HOSTED_APP\""
61      "},"
62      "{"
63        "\"id\": \"app3_id\","
64        "\"localized_name\": \"three\","
65        "\"icon_url\": \"http://host/icon3\","
66        "\"item_type\": \"LEGACY_PACKAGED_APP\""
67      "}"
68    "]}";
69
70struct ParsedSearchResult {
71  const char* id;
72  const char* title;
73  const char* icon_url;
74  Manifest::Type item_type;
75  size_t num_actions;
76};
77
78ParsedSearchResult kParsedOneResult[] = {
79  { "app1_id", "app1 name", "http://host/icon", Manifest::TYPE_UNKNOWN, 1 }
80};
81
82ParsedSearchResult kParsedThreeResults[] = {
83  { "app1_id", "one", "http://host/icon1", Manifest::TYPE_PLATFORM_APP, 2 },
84  { "app2_id", "two", "http://host/icon2", Manifest::TYPE_HOSTED_APP, 2 },
85  { "app3_id", "three", "http://host/icon3",
86    Manifest::TYPE_LEGACY_PACKAGED_APP, 1 }
87};
88
89}  // namespace
90
91class WebstoreProviderTest : public InProcessBrowserTest {
92 public:
93  WebstoreProviderTest() {}
94  virtual ~WebstoreProviderTest() {}
95
96  // InProcessBrowserTest overrides:
97  virtual void SetUpOnMainThread() OVERRIDE {
98    test_server_.reset(new EmbeddedTestServer);
99
100    ASSERT_TRUE(test_server_->InitializeAndWaitUntilReady());
101    test_server_->RegisterRequestHandler(
102        base::Bind(&WebstoreProviderTest::HandleRequest,
103                   base::Unretained(this)));
104    CommandLine::ForCurrentProcess()->AppendSwitchASCII(
105        switches::kAppsGalleryURL, test_server_->base_url().spec());
106    CommandLine::ForCurrentProcess()->AppendSwitch(
107        switches::kEnableEphemeralApps);
108
109    webstore_provider_.reset(new WebstoreProvider(
110        ProfileManager::GetActiveUserProfile(), NULL));
111    webstore_provider_->set_webstore_search_fetched_callback(
112        base::Bind(&WebstoreProviderTest::OnSearchResultsFetched,
113                   base::Unretained(this)));
114    // TODO(mukai): add test cases for throttling.
115    webstore_provider_->set_use_throttling(false);
116  }
117
118  virtual void TearDownOnMainThread() OVERRIDE {
119    EXPECT_TRUE(test_server_->ShutdownAndWaitUntilComplete());
120    test_server_.reset();
121  }
122
123  void RunQuery(const std::string& query,
124                const std::string& mock_server_response) {
125    webstore_provider_->Start(base::UTF8ToUTF16(query));
126
127    if (webstore_provider_->webstore_search_ && !mock_server_response.empty()) {
128      mock_server_response_ = mock_server_response;
129
130      DCHECK(!run_loop_);
131      run_loop_.reset(new base::RunLoop);
132      run_loop_->Run();
133      run_loop_.reset();
134
135      mock_server_response_.clear();
136    }
137
138    webstore_provider_->Stop();
139  }
140
141  std::string GetResultTitles() const {
142    std::string results;
143    for (SearchProvider::Results::const_iterator it =
144             webstore_provider_->results().begin();
145         it != webstore_provider_->results().end();
146         ++it) {
147      if (!results.empty())
148        results += ',';
149      results += base::UTF16ToUTF8((*it)->title());
150    }
151    return results;
152  }
153
154  void VerifyResults(const ParsedSearchResult* expected_results,
155                     size_t expected_result_size) {
156    ASSERT_EQ(expected_result_size, webstore_provider_->results().size());
157    for (size_t i = 0; i < expected_result_size; ++i) {
158      const SearchResult* result = webstore_provider_->results()[i];
159      ASSERT_EQ(extensions::Extension::GetBaseURLFromExtensionId(
160                    expected_results[i].id).spec(),
161                result->id());
162      EXPECT_EQ(std::string(expected_results[i].title),
163                base::UTF16ToUTF8(result->title()));
164
165      // Ensure the number of action buttons is appropriate for the item type.
166      EXPECT_EQ(expected_results[i].num_actions, result->actions().size());
167
168      const WebstoreResult* webstore_result =
169          static_cast<const WebstoreResult*>(result);
170      EXPECT_EQ(expected_results[i].id, webstore_result->app_id());
171      EXPECT_EQ(expected_results[i].icon_url,
172                webstore_result->icon_url().spec());
173      EXPECT_EQ(expected_results[i].item_type, webstore_result->item_type());
174    }
175  }
176
177  void RunQueryAndVerify(const std::string& query,
178                         const std::string& mock_server_response,
179                         const ParsedSearchResult* expected_results,
180                         size_t expected_result_size) {
181    RunQuery(query, mock_server_response);
182    VerifyResults(expected_results, expected_result_size);
183  }
184
185  WebstoreProvider* webstore_provider() { return webstore_provider_.get(); }
186
187 private:
188  scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
189    scoped_ptr<BasicHttpResponse> response(new BasicHttpResponse);
190
191    if (request.relative_url.find("/jsonsearch?") != std::string::npos) {
192      if (mock_server_response_ == "404") {
193        response->set_code(net::HTTP_NOT_FOUND);
194      } else if (mock_server_response_ == "500") {
195        response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
196      } else {
197        response->set_code(net::HTTP_OK);
198        response->set_content(mock_server_response_);
199      }
200    }
201
202    return response.PassAs<HttpResponse>();
203  }
204
205  void OnSearchResultsFetched() {
206    if (run_loop_)
207      run_loop_->Quit();
208  }
209
210  scoped_ptr<EmbeddedTestServer> test_server_;
211  scoped_ptr<base::RunLoop> run_loop_;
212
213  std::string mock_server_response_;
214
215  scoped_ptr<WebstoreProvider> webstore_provider_;
216
217  DISALLOW_COPY_AND_ASSIGN(WebstoreProviderTest);
218};
219
220// Flaky on CrOS and Windows: http://crbug.com/246136.
221// TODO(erg): linux_aura bringup: http://crbug.com/163931
222#if defined(OS_WIN) || defined(OS_LINUX)
223#define MAYBE_Basic DISABLED_Basic
224#else
225#define MAYBE_Basic Basic
226#endif
227IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, MAYBE_Basic) {
228  struct {
229    const char* query;
230    const char* mock_server_response;
231    const char* expected_result_titles;
232    const ParsedSearchResult* expected_results;
233    size_t expected_result_size;
234  } kTestCases[] = {
235    // "Search in web store" result with query text itself is used for
236    // synchronous placeholder, bad server response etc.
237    {"synchronous", "", "synchronous", NULL, 0 },
238    {"404", "404", "404", NULL, 0 },
239    {"500", "500", "500", NULL, 0 },
240    {"bad json", "invalid json", "bad json", NULL, 0 },
241    // Good results.
242    {"1 result", kOneResult, "app1 name", kParsedOneResult, 1 },
243    {"3 result", kThreeResults, "one,two,three", kParsedThreeResults, 3 },
244  };
245
246  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
247    if (kTestCases[i].expected_result_titles) {
248      RunQuery(kTestCases[i].query, kTestCases[i].mock_server_response);
249      ASSERT_EQ(kTestCases[i].expected_result_titles, GetResultTitles())
250          << "Case " << i << ": q=" << kTestCases[i].query;
251
252      if (kTestCases[i].expected_results) {
253        VerifyResults(kTestCases[i].expected_results,
254                      kTestCases[i].expected_result_size);
255      }
256    }
257  }
258}
259
260IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, NoSearchForSensitiveData) {
261  // None of the following input strings should be accepted because they may
262  // contain private data.
263  const char* inputs[] = {
264    // file: scheme is bad.
265    "file://filename",
266    "FILE://filename",
267    // URLs with usernames, ports, queries or refs are bad.
268    "http://username:password@hostname/",
269    "http://www.example.com:1000",
270    "http://foo:1000",
271    "http://hostname/?query=q",
272    "http://hostname/path#ref",
273    // A https URL with path is bad.
274    "https://hostname/path",
275  };
276
277  for (size_t i = 0; i < arraysize(inputs); ++i) {
278    RunQueryAndVerify(inputs[i], kOneResult, NULL, 0);
279  }
280}
281
282IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, NoSearchForShortQueries) {
283  RunQueryAndVerify("a", kOneResult, NULL, 0);
284  RunQueryAndVerify("ab", kOneResult, NULL, 0);
285  RunQueryAndVerify("abc", kOneResult, kParsedOneResult, 1);
286}
287
288// Flaky on CrOS and Windows: http://crbug.com/246136.
289#if defined(OS_WIN) || defined(OS_CHROMEOS)
290#define MAYBE_SearchCache DISABLED_SearchCache
291#else
292#define MAYBE_SearchCache SearchCache
293#endif
294IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, MAYBE_SearchCache) {
295  RunQueryAndVerify("foo", kOneResult, kParsedOneResult, 1);
296
297  // No result is provided but the provider gets the result from the cache.
298  RunQueryAndVerify("foo", "", kParsedOneResult, 1);
299}
300
301}  // namespace test
302}  // namespace app_list
303