Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / extensions / extension_apitest.cc
bloba317bac396330af14dd2b4814148e76f885ff045
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/extension_apitest.h"
7 #include "base/strings/string_split.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/api/test/test_api.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_system.h"
14 #include "chrome/browser/extensions/unpacked_installer.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/extensions/application_launch.h"
18 #include "chrome/test/base/ui_test_utils.h"
19 #include "content/public/browser/notification_registrar.h"
20 #include "content/public/browser/notification_service.h"
21 #include "extensions/common/extension.h"
22 #include "extensions/common/extension_set.h"
23 #include "net/base/escape.h"
24 #include "net/base/net_util.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
26 #include "net/test/embedded_test_server/http_request.h"
27 #include "net/test/embedded_test_server/http_response.h"
28 #include "net/test/spawned_test_server/spawned_test_server.h"
30 namespace {
32 const char kTestCustomArg[] = "customArg";
33 const char kTestServerPort[] = "testServer.port";
34 const char kTestDataDirectory[] = "testDataDirectory";
35 const char kTestWebSocketPort[] = "testWebSocketPort";
36 const char kSpawnedTestServerPort[] = "spawnedTestServer.port";
38 scoped_ptr<net::test_server::HttpResponse> HandleServerRedirectRequest(
39 const net::test_server::HttpRequest& request) {
40 if (!StartsWithASCII(request.relative_url, "/server-redirect?", true))
41 return scoped_ptr<net::test_server::HttpResponse>();
43 size_t query_string_pos = request.relative_url.find('?');
44 std::string redirect_target =
45 request.relative_url.substr(query_string_pos + 1);
47 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
48 new net::test_server::BasicHttpResponse);
49 http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
50 http_response->AddCustomHeader("Location", redirect_target);
51 return http_response.PassAs<net::test_server::HttpResponse>();
54 scoped_ptr<net::test_server::HttpResponse> HandleEchoHeaderRequest(
55 const net::test_server::HttpRequest& request) {
56 if (!StartsWithASCII(request.relative_url, "/echoheader?", true))
57 return scoped_ptr<net::test_server::HttpResponse>();
59 size_t query_string_pos = request.relative_url.find('?');
60 std::string header_name =
61 request.relative_url.substr(query_string_pos + 1);
63 std::string header_value;
64 std::map<std::string, std::string>::const_iterator it = request.headers.find(
65 header_name);
66 if (it != request.headers.end())
67 header_value = it->second;
69 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
70 new net::test_server::BasicHttpResponse);
71 http_response->set_code(net::HTTP_OK);
72 http_response->set_content(header_value);
73 return http_response.PassAs<net::test_server::HttpResponse>();
76 scoped_ptr<net::test_server::HttpResponse> HandleSetCookieRequest(
77 const net::test_server::HttpRequest& request) {
78 if (!StartsWithASCII(request.relative_url, "/set-cookie?", true))
79 return scoped_ptr<net::test_server::HttpResponse>();
81 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
82 new net::test_server::BasicHttpResponse);
83 http_response->set_code(net::HTTP_OK);
85 size_t query_string_pos = request.relative_url.find('?');
86 std::string cookie_value =
87 request.relative_url.substr(query_string_pos + 1);
89 std::vector<std::string> cookies;
90 base::SplitString(cookie_value, '&', &cookies);
92 for (size_t i = 0; i < cookies.size(); i++)
93 http_response->AddCustomHeader("Set-Cookie", cookies[i]);
95 return http_response.PassAs<net::test_server::HttpResponse>();
98 scoped_ptr<net::test_server::HttpResponse> HandleSetHeaderRequest(
99 const net::test_server::HttpRequest& request) {
100 if (!StartsWithASCII(request.relative_url, "/set-header?", true))
101 return scoped_ptr<net::test_server::HttpResponse>();
103 size_t query_string_pos = request.relative_url.find('?');
104 std::string escaped_header =
105 request.relative_url.substr(query_string_pos + 1);
107 std::string header =
108 net::UnescapeURLComponent(escaped_header,
109 net::UnescapeRule::NORMAL |
110 net::UnescapeRule::SPACES |
111 net::UnescapeRule::URL_SPECIAL_CHARS);
113 size_t colon_pos = header.find(':');
114 if (colon_pos == std::string::npos)
115 return scoped_ptr<net::test_server::HttpResponse>();
117 std::string header_name = header.substr(0, colon_pos);
118 // Skip space after colon.
119 std::string header_value = header.substr(colon_pos + 2);
121 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
122 new net::test_server::BasicHttpResponse);
123 http_response->set_code(net::HTTP_OK);
124 http_response->AddCustomHeader(header_name, header_value);
125 return http_response.PassAs<net::test_server::HttpResponse>();
128 }; // namespace
130 ExtensionApiTest::ExtensionApiTest() {
131 embedded_test_server()->RegisterRequestHandler(
132 base::Bind(&HandleServerRedirectRequest));
133 embedded_test_server()->RegisterRequestHandler(
134 base::Bind(&HandleEchoHeaderRequest));
135 embedded_test_server()->RegisterRequestHandler(
136 base::Bind(&HandleSetCookieRequest));
137 embedded_test_server()->RegisterRequestHandler(
138 base::Bind(&HandleSetHeaderRequest));
141 ExtensionApiTest::~ExtensionApiTest() {}
143 ExtensionApiTest::ResultCatcher::ResultCatcher()
144 : profile_restriction_(NULL),
145 waiting_(false) {
146 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_TEST_PASSED,
147 content::NotificationService::AllSources());
148 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_TEST_FAILED,
149 content::NotificationService::AllSources());
152 ExtensionApiTest::ResultCatcher::~ResultCatcher() {
155 bool ExtensionApiTest::ResultCatcher::GetNextResult() {
156 // Depending on the tests, multiple results can come in from a single call
157 // to RunMessageLoop(), so we maintain a queue of results and just pull them
158 // off as the test calls this, going to the run loop only when the queue is
159 // empty.
160 if (results_.empty()) {
161 waiting_ = true;
162 content::RunMessageLoop();
163 waiting_ = false;
166 if (!results_.empty()) {
167 bool ret = results_.front();
168 results_.pop_front();
169 message_ = messages_.front();
170 messages_.pop_front();
171 return ret;
174 NOTREACHED();
175 return false;
178 void ExtensionApiTest::ResultCatcher::Observe(
179 int type, const content::NotificationSource& source,
180 const content::NotificationDetails& details) {
181 if (profile_restriction_ &&
182 content::Source<Profile>(source).ptr() != profile_restriction_) {
183 return;
186 switch (type) {
187 case chrome::NOTIFICATION_EXTENSION_TEST_PASSED:
188 VLOG(1) << "Got EXTENSION_TEST_PASSED notification.";
189 results_.push_back(true);
190 messages_.push_back(std::string());
191 if (waiting_)
192 base::MessageLoopForUI::current()->Quit();
193 break;
195 case chrome::NOTIFICATION_EXTENSION_TEST_FAILED:
196 VLOG(1) << "Got EXTENSION_TEST_FAILED notification.";
197 results_.push_back(false);
198 messages_.push_back(*(content::Details<std::string>(details).ptr()));
199 if (waiting_)
200 base::MessageLoopForUI::current()->Quit();
201 break;
203 default:
204 NOTREACHED();
208 void ExtensionApiTest::SetUpInProcessBrowserTestFixture() {
209 DCHECK(!test_config_.get()) << "Previous test did not clear config state.";
210 test_config_.reset(new base::DictionaryValue());
211 test_config_->SetString(kTestDataDirectory,
212 net::FilePathToFileURL(test_data_dir_).spec());
213 test_config_->SetInteger(kTestWebSocketPort, 0);
214 extensions::TestGetConfigFunction::set_test_config_state(
215 test_config_.get());
218 void ExtensionApiTest::TearDownInProcessBrowserTestFixture() {
219 extensions::TestGetConfigFunction::set_test_config_state(NULL);
220 test_config_.reset(NULL);
223 bool ExtensionApiTest::RunExtensionTest(const std::string& extension_name) {
224 return RunExtensionTestImpl(
225 extension_name, std::string(), NULL, kFlagEnableFileAccess);
228 bool ExtensionApiTest::RunExtensionTestIncognito(
229 const std::string& extension_name) {
230 return RunExtensionTestImpl(extension_name,
231 std::string(),
232 NULL,
233 kFlagEnableIncognito | kFlagEnableFileAccess);
236 bool ExtensionApiTest::RunExtensionTestIgnoreManifestWarnings(
237 const std::string& extension_name) {
238 return RunExtensionTestImpl(
239 extension_name, std::string(), NULL, kFlagIgnoreManifestWarnings);
242 bool ExtensionApiTest::RunExtensionTestAllowOldManifestVersion(
243 const std::string& extension_name) {
244 return RunExtensionTestImpl(
245 extension_name,
246 std::string(),
247 NULL,
248 kFlagEnableFileAccess | kFlagAllowOldManifestVersions);
251 bool ExtensionApiTest::RunComponentExtensionTest(
252 const std::string& extension_name) {
253 return RunExtensionTestImpl(extension_name,
254 std::string(),
255 NULL,
256 kFlagEnableFileAccess | kFlagLoadAsComponent);
259 bool ExtensionApiTest::RunExtensionTestNoFileAccess(
260 const std::string& extension_name) {
261 return RunExtensionTestImpl(extension_name, std::string(), NULL, kFlagNone);
264 bool ExtensionApiTest::RunExtensionTestIncognitoNoFileAccess(
265 const std::string& extension_name) {
266 return RunExtensionTestImpl(
267 extension_name, std::string(), NULL, kFlagEnableIncognito);
270 bool ExtensionApiTest::RunExtensionSubtest(const std::string& extension_name,
271 const std::string& page_url) {
272 return RunExtensionSubtest(extension_name, page_url, kFlagEnableFileAccess);
275 bool ExtensionApiTest::RunExtensionSubtest(const std::string& extension_name,
276 const std::string& page_url,
277 int flags) {
278 DCHECK(!page_url.empty()) << "Argument page_url is required.";
279 // See http://crbug.com/177163 for details.
280 #if defined(OS_WIN) && !defined(NDEBUG)
281 LOG(WARNING) << "Workaround for 177163, prematurely returning";
282 return true;
283 #endif
284 return RunExtensionTestImpl(extension_name, page_url, NULL, flags);
288 bool ExtensionApiTest::RunPageTest(const std::string& page_url) {
289 return RunExtensionSubtest(std::string(), page_url);
292 bool ExtensionApiTest::RunPageTest(const std::string& page_url,
293 int flags) {
294 return RunExtensionSubtest(std::string(), page_url, flags);
297 bool ExtensionApiTest::RunPlatformAppTest(const std::string& extension_name) {
298 return RunExtensionTestImpl(
299 extension_name, std::string(), NULL, kFlagLaunchPlatformApp);
302 bool ExtensionApiTest::RunPlatformAppTestWithArg(
303 const std::string& extension_name, const char* custom_arg) {
304 return RunExtensionTestImpl(
305 extension_name, std::string(), custom_arg, kFlagLaunchPlatformApp);
308 bool ExtensionApiTest::RunPlatformAppTestWithFlags(
309 const std::string& extension_name, int flags) {
310 return RunExtensionTestImpl(
311 extension_name, std::string(), NULL, flags | kFlagLaunchPlatformApp);
314 // Load |extension_name| extension and/or |page_url| and wait for
315 // PASSED or FAILED notification.
316 bool ExtensionApiTest::RunExtensionTestImpl(const std::string& extension_name,
317 const std::string& page_url,
318 const char* custom_arg,
319 int flags) {
320 bool load_as_component = (flags & kFlagLoadAsComponent) != 0;
321 bool launch_platform_app = (flags & kFlagLaunchPlatformApp) != 0;
322 bool use_incognito = (flags & kFlagUseIncognito) != 0;
324 if (custom_arg && custom_arg[0])
325 test_config_->SetString(kTestCustomArg, custom_arg);
327 ResultCatcher catcher;
328 DCHECK(!extension_name.empty() || !page_url.empty()) <<
329 "extension_name and page_url cannot both be empty";
331 const extensions::Extension* extension = NULL;
332 if (!extension_name.empty()) {
333 base::FilePath extension_path = test_data_dir_.AppendASCII(extension_name);
334 if (load_as_component) {
335 extension = LoadExtensionAsComponent(extension_path);
336 } else {
337 int browser_test_flags = ExtensionBrowserTest::kFlagNone;
338 if (flags & kFlagEnableIncognito)
339 browser_test_flags |= ExtensionBrowserTest::kFlagEnableIncognito;
340 if (flags & kFlagEnableFileAccess)
341 browser_test_flags |= ExtensionBrowserTest::kFlagEnableFileAccess;
342 if (flags & kFlagIgnoreManifestWarnings)
343 browser_test_flags |= ExtensionBrowserTest::kFlagIgnoreManifestWarnings;
344 if (flags & kFlagAllowOldManifestVersions) {
345 browser_test_flags |=
346 ExtensionBrowserTest::kFlagAllowOldManifestVersions;
348 extension = LoadExtensionWithFlags(extension_path, browser_test_flags);
350 if (!extension) {
351 message_ = "Failed to load extension.";
352 return false;
356 // If there is a page_url to load, navigate it.
357 if (!page_url.empty()) {
358 GURL url = GURL(page_url);
360 // Note: We use is_valid() here in the expectation that the provided url
361 // may lack a scheme & host and thus be a relative url within the loaded
362 // extension.
363 if (!url.is_valid()) {
364 DCHECK(!extension_name.empty()) <<
365 "Relative page_url given with no extension_name";
367 url = extension->GetResourceURL(page_url);
370 if (use_incognito)
371 ui_test_utils::OpenURLOffTheRecord(browser()->profile(), url);
372 else
373 ui_test_utils::NavigateToURL(browser(), url);
374 } else if (launch_platform_app) {
375 AppLaunchParams params(browser()->profile(),
376 extension,
377 extensions::LAUNCH_CONTAINER_NONE,
378 NEW_WINDOW);
379 params.command_line = *CommandLine::ForCurrentProcess();
380 OpenApplication(params);
383 if (!catcher.GetNextResult()) {
384 message_ = catcher.message();
385 return false;
388 return true;
391 // Test that exactly one extension is loaded, and return it.
392 const extensions::Extension* ExtensionApiTest::GetSingleLoadedExtension() {
393 ExtensionService* service = extensions::ExtensionSystem::Get(
394 browser()->profile())->extension_service();
396 const extensions::Extension* extension = NULL;
397 for (extensions::ExtensionSet::const_iterator it =
398 service->extensions()->begin();
399 it != service->extensions()->end(); ++it) {
400 // Ignore any component extensions. They are automatically loaded into all
401 // profiles and aren't the extension we're looking for here.
402 if ((*it)->location() == extensions::Manifest::COMPONENT)
403 continue;
405 if (extension != NULL) {
406 // TODO(yoz): this is misleading; it counts component extensions.
407 message_ = base::StringPrintf(
408 "Expected only one extension to be present. Found %u.",
409 static_cast<unsigned>(service->extensions()->size()));
410 return NULL;
413 extension = it->get();
416 if (!extension) {
417 message_ = "extension pointer is NULL.";
418 return NULL;
420 return extension;
423 bool ExtensionApiTest::StartEmbeddedTestServer() {
424 if (!embedded_test_server()->InitializeAndWaitUntilReady())
425 return false;
427 // Build a dictionary of values that tests can use to build URLs that
428 // access the test server and local file system. Tests can see these values
429 // using the extension API function chrome.test.getConfig().
430 test_config_->SetInteger(kTestServerPort,
431 embedded_test_server()->port());
433 return true;
436 bool ExtensionApiTest::StartWebSocketServer(
437 const base::FilePath& root_directory) {
438 websocket_server_.reset(new net::SpawnedTestServer(
439 net::SpawnedTestServer::TYPE_WS,
440 net::SpawnedTestServer::kLocalhost,
441 root_directory));
443 if (!websocket_server_->Start())
444 return false;
446 test_config_->SetInteger(kTestWebSocketPort,
447 websocket_server_->host_port_pair().port());
449 return true;
452 bool ExtensionApiTest::StartSpawnedTestServer() {
453 if (!test_server()->Start())
454 return false;
456 // Build a dictionary of values that tests can use to build URLs that
457 // access the test server and local file system. Tests can see these values
458 // using the extension API function chrome.test.getConfig().
459 test_config_->SetInteger(kSpawnedTestServerPort,
460 test_server()->host_port_pair().port());
462 return true;
465 void ExtensionApiTest::SetUpCommandLine(CommandLine* command_line) {
466 ExtensionBrowserTest::SetUpCommandLine(command_line);
467 test_data_dir_ = test_data_dir_.AppendASCII("api_test");