webstore_provider_browsertest.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/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
23using content::BrowserThread;
24using net::test_server::BasicHttpResponse;
25using net::test_server::HttpRequest;
26using net::test_server::HttpResponse;
27using net::test_server::EmbeddedTestServer;
28
29namespace app_list {
30namespace test {
31namespace {
32
33// Mock results.
34const char kOneResult[] = "{"
35    "\"search_url\": \"http://host/search\","
36    "\"results\":["
37      "{"
38        "\"id\": \"app1_id\","
39        "\"localized_name\": \"app1 name\","
40        "\"icon_url\": \"http://host/icon\""
41      "}"
42    "]}";
43
44const char kThreeResults[] = "{"
45    "\"search_url\": \"http://host/search\","
46    "\"results\":["
47      "{"
48        "\"id\": \"app1_id\","
49        "\"localized_name\": \"one\","
50        "\"icon_url\": \"http://host/icon\""
51      "},"
52      "{"
53        "\"id\": \"app2_id\","
54        "\"localized_name\": \"two\","
55        "\"icon_url\": \"http://host/icon\""
56      "},"
57      "{"
58        "\"id\": \"app3_id\","
59        "\"localized_name\": \"three\","
60        "\"icon_url\": \"http://host/icon\""
61      "}"
62    "]}";
63
64}  // namespace
65
66class WebstoreProviderTest : public InProcessBrowserTest {
67 public:
68  WebstoreProviderTest() {}
69  virtual ~WebstoreProviderTest() {}
70
71  // InProcessBrowserTest overrides:
72  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
73    command_line->AppendSwitchASCII(switches::kForceFieldTrials,
74                                    "LauncherUseWebstoreSearch/Enable/");
75  }
76
77  virtual void SetUpOnMainThread() OVERRIDE {
78    test_server_.reset(new EmbeddedTestServer(
79        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)));
80
81    ASSERT_TRUE(test_server_->InitializeAndWaitUntilReady());
82    test_server_->RegisterRequestHandler(
83        base::Bind(&WebstoreProviderTest::HandleRequest,
84                   base::Unretained(this)));
85    CommandLine::ForCurrentProcess()->AppendSwitchASCII(
86        switches::kAppsGalleryURL, test_server_->base_url().spec());
87
88    webstore_provider_.reset(new WebstoreProvider(
89        ProfileManager::GetDefaultProfile(), NULL));
90    webstore_provider_->set_webstore_search_fetched_callback(
91        base::Bind(&WebstoreProviderTest::OnSearchResultsFetched,
92                   base::Unretained(this)));
93    // TODO(mukai): add test cases for throttling.
94    webstore_provider_->set_use_throttling(false);
95  }
96
97  virtual void CleanUpOnMainThread() OVERRIDE {
98    EXPECT_TRUE(test_server_->ShutdownAndWaitUntilComplete());
99    test_server_.reset();
100  }
101
102  std::string RunQuery(const std::string& query,
103                       const std::string& mock_server_response) {
104    webstore_provider_->Start(UTF8ToUTF16(query));
105
106    if (webstore_provider_->webstore_search_ && !mock_server_response.empty()) {
107      mock_server_response_ = mock_server_response;
108
109      DCHECK(!run_loop_);
110      run_loop_.reset(new base::RunLoop);
111      run_loop_->Run();
112      run_loop_.reset();
113
114      mock_server_response_.clear();
115    }
116
117    webstore_provider_->Stop();
118    return GetResults();
119  }
120
121  std::string GetResults() const {
122    std::string results;
123    for (SearchProvider::Results::const_iterator it =
124             webstore_provider_->results().begin();
125         it != webstore_provider_->results().end();
126         ++it) {
127      if (!results.empty())
128        results += ',';
129      results += UTF16ToUTF8((*it)->title());
130    }
131    return results;
132  }
133
134  WebstoreProvider* webstore_provider() { return webstore_provider_.get(); }
135
136 private:
137  scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
138    scoped_ptr<BasicHttpResponse> response(new BasicHttpResponse);
139
140    if (request.relative_url.find("/jsonsearch?") != std::string::npos) {
141      if (mock_server_response_ == "404") {
142        response->set_code(net::HTTP_NOT_FOUND);
143      } else if (mock_server_response_ == "500") {
144        response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
145      } else {
146        response->set_code(net::HTTP_OK);
147        response->set_content(mock_server_response_);
148      }
149    }
150
151    return response.PassAs<HttpResponse>();
152  }
153
154  void OnSearchResultsFetched() {
155    if (run_loop_)
156      run_loop_->Quit();
157  }
158
159  scoped_ptr<EmbeddedTestServer> test_server_;
160  scoped_ptr<base::RunLoop> run_loop_;
161
162  std::string mock_server_response_;
163
164  scoped_ptr<WebstoreProvider> webstore_provider_;
165
166  DISALLOW_COPY_AND_ASSIGN(WebstoreProviderTest);
167};
168
169// Flaky on CrOS and Windows: http://crbug.com/246136.
170#if defined(OS_WIN) || defined(OS_CHROMEOS)
171#define MAYBE_Basic DISABLED_Basic
172#else
173#define MAYBE_Basic Basic
174#endif
175IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, MAYBE_Basic) {
176  struct {
177    const char* query;
178    const char* mock_server_response;
179    const char* expected_results_content;
180  } kTestCases[] = {
181    // "Search in web store" result with query text itself is used for
182    // synchronous placeholder, bad server response etc.
183    {"synchronous", "", "synchronous" },
184    {"404", "404", "404" },
185    {"500", "500", "500" },
186    {"bad json", "invalid json", "bad json" },
187    // Good results.
188    {"1 result", kOneResult, "app1 name" },
189    {"3 result", kThreeResults, "one,two,three" },
190  };
191
192  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
193    EXPECT_EQ(kTestCases[i].expected_results_content,
194              RunQuery(kTestCases[i].query,
195                       kTestCases[i].mock_server_response))
196        << "Case " << i << ": q=" << kTestCases[i].query;
197  }
198}
199
200IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, NoSearchForSensitiveData) {
201  // None of the following input strings should be accepted because they may
202  // contain private data.
203  const char* inputs[] = {
204    // file: scheme is bad.
205    "file://filename",
206    "FILE://filename",
207    // URLs with usernames, ports, queries or refs are bad.
208    "http://username:password@hostname/",
209    "http://www.example.com:1000",
210    "http://foo:1000",
211    "http://hostname/?query=q",
212    "http://hostname/path#ref",
213    // A https URL with path is bad.
214    "https://hostname/path",
215  };
216
217  for (size_t i = 0; i < arraysize(inputs); ++i)
218    EXPECT_EQ("", RunQuery(inputs[i], kOneResult));
219}
220
221IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, NoSearchForShortQueries) {
222  EXPECT_EQ("", RunQuery("a", kOneResult));
223  EXPECT_EQ("", RunQuery("ab", kOneResult));
224  EXPECT_EQ("app1 name", RunQuery("abc", kOneResult));
225}
226
227// Flaky on CrOS and Windows: http://crbug.com/246136.
228#if defined(OS_WIN) || defined(OS_CHROMEOS)
229#define MAYBE_SearchCache DISABLED_SearchCache
230#else
231#define MAYBE_SearchCache SearchCache
232#endif
233IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, MAYBE_SearchCache) {
234  EXPECT_EQ("app1 name", RunQuery("foo", kOneResult));
235
236  // No result is provided but the provider gets the result from the cache.
237  EXPECT_EQ("app1 name", RunQuery("foo", ""));
238}
239
240}  // namespace test
241}  // namespace app_list
242