1 // Copyright (c) 2011 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 "chrome/browser/extensions/extension_apitest.h" 6 7 #include "base/string_util.h" 8 #include "base/stringprintf.h" 9 #include "chrome/browser/extensions/extension_service.h" 10 #include "chrome/browser/extensions/extension_test_api.h" 11 #include "chrome/browser/profiles/profile.h" 12 #include "chrome/browser/ui/browser.h" 13 #include "chrome/test/ui_test_utils.h" 14 #include "content/common/notification_registrar.h" 15 16 namespace { 17 18 const char kTestServerPort[] = "testServer.port"; 19 20 }; // namespace 21 22 ExtensionApiTest::ExtensionApiTest() {} 23 24 ExtensionApiTest::~ExtensionApiTest() {} 25 26 ExtensionApiTest::ResultCatcher::ResultCatcher() 27 : profile_restriction_(NULL), 28 waiting_(false) { 29 registrar_.Add(this, NotificationType::EXTENSION_TEST_PASSED, 30 NotificationService::AllSources()); 31 registrar_.Add(this, NotificationType::EXTENSION_TEST_FAILED, 32 NotificationService::AllSources()); 33 } 34 35 ExtensionApiTest::ResultCatcher::~ResultCatcher() { 36 } 37 38 bool ExtensionApiTest::ResultCatcher::GetNextResult() { 39 // Depending on the tests, multiple results can come in from a single call 40 // to RunMessageLoop(), so we maintain a queue of results and just pull them 41 // off as the test calls this, going to the run loop only when the queue is 42 // empty. 43 if (results_.empty()) { 44 waiting_ = true; 45 ui_test_utils::RunMessageLoop(); 46 waiting_ = false; 47 } 48 49 if (!results_.empty()) { 50 bool ret = results_.front(); 51 results_.pop_front(); 52 message_ = messages_.front(); 53 messages_.pop_front(); 54 return ret; 55 } 56 57 NOTREACHED(); 58 return false; 59 } 60 61 void ExtensionApiTest::ResultCatcher::Observe( 62 NotificationType type, const NotificationSource& source, 63 const NotificationDetails& details) { 64 if (profile_restriction_ && 65 Source<Profile>(source).ptr() != profile_restriction_) { 66 return; 67 } 68 69 switch (type.value) { 70 case NotificationType::EXTENSION_TEST_PASSED: 71 VLOG(1) << "Got EXTENSION_TEST_PASSED notification."; 72 results_.push_back(true); 73 messages_.push_back(""); 74 if (waiting_) 75 MessageLoopForUI::current()->Quit(); 76 break; 77 78 case NotificationType::EXTENSION_TEST_FAILED: 79 VLOG(1) << "Got EXTENSION_TEST_FAILED notification."; 80 results_.push_back(false); 81 messages_.push_back(*(Details<std::string>(details).ptr())); 82 if (waiting_) 83 MessageLoopForUI::current()->Quit(); 84 break; 85 86 default: 87 NOTREACHED(); 88 } 89 } 90 91 void ExtensionApiTest::SetUpInProcessBrowserTestFixture() { 92 DCHECK(!test_config_.get()) << "Previous test did not clear config state."; 93 test_config_.reset(new DictionaryValue()); 94 ExtensionTestGetConfigFunction::set_test_config_state(test_config_.get()); 95 } 96 97 void ExtensionApiTest::TearDownInProcessBrowserTestFixture() { 98 ExtensionTestGetConfigFunction::set_test_config_state(NULL); 99 test_config_.reset(NULL); 100 } 101 102 bool ExtensionApiTest::RunExtensionTest(const char* extension_name) { 103 return RunExtensionTestImpl(extension_name, "", false, true, false); 104 } 105 106 bool ExtensionApiTest::RunExtensionTestIncognito(const char* extension_name) { 107 return RunExtensionTestImpl(extension_name, "", true, true, false); 108 } 109 110 bool ExtensionApiTest::RunComponentExtensionTest(const char* extension_name) { 111 return RunExtensionTestImpl(extension_name, "", false, true, true); 112 } 113 114 bool ExtensionApiTest::RunExtensionTestNoFileAccess( 115 const char* extension_name) { 116 return RunExtensionTestImpl(extension_name, "", false, false, false); 117 } 118 119 bool ExtensionApiTest::RunExtensionTestIncognitoNoFileAccess( 120 const char* extension_name) { 121 return RunExtensionTestImpl(extension_name, "", true, false, false); 122 } 123 bool ExtensionApiTest::RunExtensionSubtest(const char* extension_name, 124 const std::string& page_url) { 125 DCHECK(!page_url.empty()) << "Argument page_url is required."; 126 return RunExtensionTestImpl(extension_name, page_url, false, true, false); 127 } 128 129 bool ExtensionApiTest::RunPageTest(const std::string& page_url) { 130 return RunExtensionSubtest("", page_url); 131 } 132 133 // Load |extension_name| extension and/or |page_url| and wait for 134 // PASSED or FAILED notification. 135 bool ExtensionApiTest::RunExtensionTestImpl(const char* extension_name, 136 const std::string& page_url, 137 bool enable_incognito, 138 bool enable_fileaccess, 139 bool load_as_component) { 140 ResultCatcher catcher; 141 DCHECK(!std::string(extension_name).empty() || !page_url.empty()) << 142 "extension_name and page_url cannot both be empty"; 143 144 if (!std::string(extension_name).empty()) { 145 bool loaded = false; 146 if (load_as_component) { 147 loaded = 148 LoadExtensionAsComponent(test_data_dir_.AppendASCII(extension_name)); 149 } else { 150 if (enable_incognito) { 151 loaded = enable_fileaccess ? 152 LoadExtensionIncognito(test_data_dir_.AppendASCII(extension_name)) : 153 LoadExtensionIncognitoNoFileAccess( 154 test_data_dir_.AppendASCII(extension_name)); 155 } else { 156 loaded = enable_fileaccess ? 157 LoadExtension(test_data_dir_.AppendASCII(extension_name)) : 158 LoadExtensionNoFileAccess(test_data_dir_.AppendASCII(extension_name)); 159 } 160 } 161 if (!loaded) { 162 message_ = "Failed to load extension."; 163 return false; 164 } 165 } 166 167 // If there is a page_url to load, navigate it. 168 if (!page_url.empty()) { 169 GURL url = GURL(page_url); 170 171 // Note: We use is_valid() here in the expectation that the provided url 172 // may lack a scheme & host and thus be a relative url within the loaded 173 // extension. 174 if (!url.is_valid()) { 175 DCHECK(!std::string(extension_name).empty()) << 176 "Relative page_url given with no extension_name"; 177 178 ExtensionService* service = browser()->profile()->GetExtensionService(); 179 const Extension* extension = 180 service->GetExtensionById(last_loaded_extension_id_, false); 181 if (!extension) 182 return false; 183 184 url = extension->GetResourceURL(page_url); 185 } 186 187 ui_test_utils::NavigateToURL(browser(), url); 188 } 189 190 if (!catcher.GetNextResult()) { 191 message_ = catcher.message(); 192 return false; 193 } else { 194 return true; 195 } 196 } 197 198 // Test that exactly one extension loaded. 199 const Extension* ExtensionApiTest::GetSingleLoadedExtension() { 200 ExtensionService* service = browser()->profile()->GetExtensionService(); 201 202 int found_extension_index = -1; 203 for (size_t i = 0; i < service->extensions()->size(); ++i) { 204 // Ignore any component extensions. They are automatically loaded into all 205 // profiles and aren't the extension we're looking for here. 206 if (service->extensions()->at(i)->location() == Extension::COMPONENT) 207 continue; 208 209 if (found_extension_index != -1) { 210 message_ = base::StringPrintf( 211 "Expected only one extension to be present. Found %u.", 212 static_cast<unsigned>(service->extensions()->size())); 213 return NULL; 214 } 215 216 found_extension_index = static_cast<int>(i); 217 } 218 219 const Extension* extension = service->extensions()->at(found_extension_index); 220 if (!extension) { 221 message_ = "extension pointer is NULL."; 222 return NULL; 223 } 224 return extension; 225 } 226 227 bool ExtensionApiTest::StartTestServer() { 228 if (!test_server()->Start()) 229 return false; 230 231 // Build a dictionary of values that tests can use to build URLs that 232 // access the test server. Tests can see these values using the extension 233 // API function chrome.test.getConfig(). 234 test_config_->SetInteger(kTestServerPort, 235 test_server()->host_port_pair().port()); 236 237 return true; 238 } 239 240 void ExtensionApiTest::SetUpCommandLine(CommandLine* command_line) { 241 ExtensionBrowserTest::SetUpCommandLine(command_line); 242 test_data_dir_ = test_data_dir_.AppendASCII("api_test"); 243 } 244