// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/extension_apitest.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_test_api.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/ui_test_utils.h" #include "content/common/notification_registrar.h" namespace { const char kTestServerPort[] = "testServer.port"; }; // namespace ExtensionApiTest::ExtensionApiTest() {} ExtensionApiTest::~ExtensionApiTest() {} ExtensionApiTest::ResultCatcher::ResultCatcher() : profile_restriction_(NULL), waiting_(false) { registrar_.Add(this, NotificationType::EXTENSION_TEST_PASSED, NotificationService::AllSources()); registrar_.Add(this, NotificationType::EXTENSION_TEST_FAILED, NotificationService::AllSources()); } ExtensionApiTest::ResultCatcher::~ResultCatcher() { } bool ExtensionApiTest::ResultCatcher::GetNextResult() { // Depending on the tests, multiple results can come in from a single call // to RunMessageLoop(), so we maintain a queue of results and just pull them // off as the test calls this, going to the run loop only when the queue is // empty. if (results_.empty()) { waiting_ = true; ui_test_utils::RunMessageLoop(); waiting_ = false; } if (!results_.empty()) { bool ret = results_.front(); results_.pop_front(); message_ = messages_.front(); messages_.pop_front(); return ret; } NOTREACHED(); return false; } void ExtensionApiTest::ResultCatcher::Observe( NotificationType type, const NotificationSource& source, const NotificationDetails& details) { if (profile_restriction_ && Source<Profile>(source).ptr() != profile_restriction_) { return; } switch (type.value) { case NotificationType::EXTENSION_TEST_PASSED: VLOG(1) << "Got EXTENSION_TEST_PASSED notification."; results_.push_back(true); messages_.push_back(""); if (waiting_) MessageLoopForUI::current()->Quit(); break; case NotificationType::EXTENSION_TEST_FAILED: VLOG(1) << "Got EXTENSION_TEST_FAILED notification."; results_.push_back(false); messages_.push_back(*(Details<std::string>(details).ptr())); if (waiting_) MessageLoopForUI::current()->Quit(); break; default: NOTREACHED(); } } void ExtensionApiTest::SetUpInProcessBrowserTestFixture() { DCHECK(!test_config_.get()) << "Previous test did not clear config state."; test_config_.reset(new DictionaryValue()); ExtensionTestGetConfigFunction::set_test_config_state(test_config_.get()); } void ExtensionApiTest::TearDownInProcessBrowserTestFixture() { ExtensionTestGetConfigFunction::set_test_config_state(NULL); test_config_.reset(NULL); } bool ExtensionApiTest::RunExtensionTest(const char* extension_name) { return RunExtensionTestImpl(extension_name, "", false, true, false); } bool ExtensionApiTest::RunExtensionTestIncognito(const char* extension_name) { return RunExtensionTestImpl(extension_name, "", true, true, false); } bool ExtensionApiTest::RunComponentExtensionTest(const char* extension_name) { return RunExtensionTestImpl(extension_name, "", false, true, true); } bool ExtensionApiTest::RunExtensionTestNoFileAccess( const char* extension_name) { return RunExtensionTestImpl(extension_name, "", false, false, false); } bool ExtensionApiTest::RunExtensionTestIncognitoNoFileAccess( const char* extension_name) { return RunExtensionTestImpl(extension_name, "", true, false, false); } bool ExtensionApiTest::RunExtensionSubtest(const char* extension_name, const std::string& page_url) { DCHECK(!page_url.empty()) << "Argument page_url is required."; return RunExtensionTestImpl(extension_name, page_url, false, true, false); } bool ExtensionApiTest::RunPageTest(const std::string& page_url) { return RunExtensionSubtest("", page_url); } // Load |extension_name| extension and/or |page_url| and wait for // PASSED or FAILED notification. bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name, const std::string& page_url, bool enable_incognito, bool enable_fileaccess, bool load_as_component) { ResultCatcher catcher; DCHECK(!std::string(extension_name).empty() || !page_url.empty()) << "extension_name and page_url cannot both be empty"; if (!std::string(extension_name).empty()) { bool loaded = false; if (load_as_component) { loaded = LoadExtensionAsComponent(test_data_dir_.AppendASCII(extension_name)); } else { if (enable_incognito) { loaded = enable_fileaccess ? LoadExtensionIncognito(test_data_dir_.AppendASCII(extension_name)) : LoadExtensionIncognitoNoFileAccess( test_data_dir_.AppendASCII(extension_name)); } else { loaded = enable_fileaccess ? LoadExtension(test_data_dir_.AppendASCII(extension_name)) : LoadExtensionNoFileAccess(test_data_dir_.AppendASCII(extension_name)); } } if (!loaded) { message_ = "Failed to load extension."; return false; } } // If there is a page_url to load, navigate it. if (!page_url.empty()) { GURL url = GURL(page_url); // Note: We use is_valid() here in the expectation that the provided url // may lack a scheme & host and thus be a relative url within the loaded // extension. if (!url.is_valid()) { DCHECK(!std::string(extension_name).empty()) << "Relative page_url given with no extension_name"; ExtensionService* service = browser()->profile()->GetExtensionService(); const Extension* extension = service->GetExtensionById(last_loaded_extension_id_, false); if (!extension) return false; url = extension->GetResourceURL(page_url); } ui_test_utils::NavigateToURL(browser(), url); } if (!catcher.GetNextResult()) { message_ = catcher.message(); return false; } else { return true; } } // Test that exactly one extension loaded. const Extension* ExtensionApiTest::GetSingleLoadedExtension() { ExtensionService* service = browser()->profile()->GetExtensionService(); int found_extension_index = -1; for (size_t i = 0; i < service->extensions()->size(); ++i) { // Ignore any component extensions. They are automatically loaded into all // profiles and aren't the extension we're looking for here. if (service->extensions()->at(i)->location() == Extension::COMPONENT) continue; if (found_extension_index != -1) { message_ = base::StringPrintf( "Expected only one extension to be present. Found %u.", static_cast<unsigned>(service->extensions()->size())); return NULL; } found_extension_index = static_cast<int>(i); } const Extension* extension = service->extensions()->at(found_extension_index); if (!extension) { message_ = "extension pointer is NULL."; return NULL; } return extension; } bool ExtensionApiTest::StartTestServer() { if (!test_server()->Start()) return false; // Build a dictionary of values that tests can use to build URLs that // access the test server. Tests can see these values using the extension // API function chrome.test.getConfig(). test_config_->SetInteger(kTestServerPort, test_server()->host_port_pair().port()); return true; } void ExtensionApiTest::SetUpCommandLine(CommandLine* command_line) { ExtensionBrowserTest::SetUpCommandLine(command_line); test_data_dir_ = test_data_dir_.AppendASCII("api_test"); }