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