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
ExtensionApiTest()22 ExtensionApiTest::ExtensionApiTest() {}
23
~ExtensionApiTest()24 ExtensionApiTest::~ExtensionApiTest() {}
25
ResultCatcher()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
~ResultCatcher()35 ExtensionApiTest::ResultCatcher::~ResultCatcher() {
36 }
37
GetNextResult()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
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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
SetUpInProcessBrowserTestFixture()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
TearDownInProcessBrowserTestFixture()97 void ExtensionApiTest::TearDownInProcessBrowserTestFixture() {
98 ExtensionTestGetConfigFunction::set_test_config_state(NULL);
99 test_config_.reset(NULL);
100 }
101
RunExtensionTest(const char * extension_name)102 bool ExtensionApiTest::RunExtensionTest(const char* extension_name) {
103 return RunExtensionTestImpl(extension_name, "", false, true, false);
104 }
105
RunExtensionTestIncognito(const char * extension_name)106 bool ExtensionApiTest::RunExtensionTestIncognito(const char* extension_name) {
107 return RunExtensionTestImpl(extension_name, "", true, true, false);
108 }
109
RunComponentExtensionTest(const char * extension_name)110 bool ExtensionApiTest::RunComponentExtensionTest(const char* extension_name) {
111 return RunExtensionTestImpl(extension_name, "", false, true, true);
112 }
113
RunExtensionTestNoFileAccess(const char * extension_name)114 bool ExtensionApiTest::RunExtensionTestNoFileAccess(
115 const char* extension_name) {
116 return RunExtensionTestImpl(extension_name, "", false, false, false);
117 }
118
RunExtensionTestIncognitoNoFileAccess(const char * extension_name)119 bool ExtensionApiTest::RunExtensionTestIncognitoNoFileAccess(
120 const char* extension_name) {
121 return RunExtensionTestImpl(extension_name, "", true, false, false);
122 }
RunExtensionSubtest(const char * extension_name,const std::string & page_url)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
RunPageTest(const std::string & page_url)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.
RunExtensionTestImpl(const char * extension_name,const std::string & page_url,bool enable_incognito,bool enable_fileaccess,bool load_as_component)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.
GetSingleLoadedExtension()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
StartTestServer()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
SetUpCommandLine(CommandLine * command_line)240 void ExtensionApiTest::SetUpCommandLine(CommandLine* command_line) {
241 ExtensionBrowserTest::SetUpCommandLine(command_line);
242 test_data_dir_ = test_data_dir_.AppendASCII("api_test");
243 }
244