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 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 "\"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 44 const 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 66 class 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 80 ASSERT_TRUE(test_server_->InitializeAndWaitUntilReady()); 81 test_server_->RegisterRequestHandler( 82 base::Bind(&WebstoreProviderTest::HandleRequest, 83 base::Unretained(this))); 84 CommandLine::ForCurrentProcess()->AppendSwitchASCII( 85 switches::kAppsGalleryURL, test_server_->base_url().spec()); 86 87 webstore_provider_.reset(new WebstoreProvider( 88 ProfileManager::GetActiveUserProfile(), NULL)); 89 webstore_provider_->set_webstore_search_fetched_callback( 90 base::Bind(&WebstoreProviderTest::OnSearchResultsFetched, 91 base::Unretained(this))); 92 // TODO(mukai): add test cases for throttling. 93 webstore_provider_->set_use_throttling(false); 94 } 95 96 virtual void CleanUpOnMainThread() OVERRIDE { 97 EXPECT_TRUE(test_server_->ShutdownAndWaitUntilComplete()); 98 test_server_.reset(); 99 } 100 101 std::string RunQuery(const std::string& query, 102 const std::string& mock_server_response) { 103 webstore_provider_->Start(base::UTF8ToUTF16(query)); 104 105 if (webstore_provider_->webstore_search_ && !mock_server_response.empty()) { 106 mock_server_response_ = mock_server_response; 107 108 DCHECK(!run_loop_); 109 run_loop_.reset(new base::RunLoop); 110 run_loop_->Run(); 111 run_loop_.reset(); 112 113 mock_server_response_.clear(); 114 } 115 116 webstore_provider_->Stop(); 117 return GetResults(); 118 } 119 120 std::string GetResults() const { 121 std::string results; 122 for (SearchProvider::Results::const_iterator it = 123 webstore_provider_->results().begin(); 124 it != webstore_provider_->results().end(); 125 ++it) { 126 if (!results.empty()) 127 results += ','; 128 results += base::UTF16ToUTF8((*it)->title()); 129 } 130 return results; 131 } 132 133 WebstoreProvider* webstore_provider() { return webstore_provider_.get(); } 134 135 private: 136 scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) { 137 scoped_ptr<BasicHttpResponse> response(new BasicHttpResponse); 138 139 if (request.relative_url.find("/jsonsearch?") != std::string::npos) { 140 if (mock_server_response_ == "404") { 141 response->set_code(net::HTTP_NOT_FOUND); 142 } else if (mock_server_response_ == "500") { 143 response->set_code(net::HTTP_INTERNAL_SERVER_ERROR); 144 } else { 145 response->set_code(net::HTTP_OK); 146 response->set_content(mock_server_response_); 147 } 148 } 149 150 return response.PassAs<HttpResponse>(); 151 } 152 153 void OnSearchResultsFetched() { 154 if (run_loop_) 155 run_loop_->Quit(); 156 } 157 158 scoped_ptr<EmbeddedTestServer> test_server_; 159 scoped_ptr<base::RunLoop> run_loop_; 160 161 std::string mock_server_response_; 162 163 scoped_ptr<WebstoreProvider> webstore_provider_; 164 165 DISALLOW_COPY_AND_ASSIGN(WebstoreProviderTest); 166 }; 167 168 // Flaky on CrOS and Windows: http://crbug.com/246136. 169 // TODO(erg): linux_aura bringup: http://crbug.com/163931 170 #if defined(OS_WIN) || defined(OS_LINUX) 171 #define MAYBE_Basic DISABLED_Basic 172 #else 173 #define MAYBE_Basic Basic 174 #endif 175 IN_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 200 IN_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 221 IN_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 233 IN_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