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