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
16namespace {
17
18const char kTestServerPort[] = "testServer.port";
19
20};  // namespace
21
22ExtensionApiTest::ExtensionApiTest() {}
23
24ExtensionApiTest::~ExtensionApiTest() {}
25
26ExtensionApiTest::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
35ExtensionApiTest::ResultCatcher::~ResultCatcher() {
36}
37
38bool 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
61void 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
91void 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
97void ExtensionApiTest::TearDownInProcessBrowserTestFixture() {
98  ExtensionTestGetConfigFunction::set_test_config_state(NULL);
99  test_config_.reset(NULL);
100}
101
102bool ExtensionApiTest::RunExtensionTest(const char* extension_name) {
103  return RunExtensionTestImpl(extension_name, "", false, true, false);
104}
105
106bool ExtensionApiTest::RunExtensionTestIncognito(const char* extension_name) {
107  return RunExtensionTestImpl(extension_name, "", true, true, false);
108}
109
110bool ExtensionApiTest::RunComponentExtensionTest(const char* extension_name) {
111  return RunExtensionTestImpl(extension_name, "", false, true, true);
112}
113
114bool ExtensionApiTest::RunExtensionTestNoFileAccess(
115    const char* extension_name) {
116  return RunExtensionTestImpl(extension_name, "", false, false, false);
117}
118
119bool ExtensionApiTest::RunExtensionTestIncognitoNoFileAccess(
120    const char* extension_name) {
121  return RunExtensionTestImpl(extension_name, "", true, false, false);
122}
123bool 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
129bool 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.
135bool 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.
199const 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
227bool 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
240void ExtensionApiTest::SetUpCommandLine(CommandLine* command_line) {
241  ExtensionBrowserTest::SetUpCommandLine(command_line);
242  test_data_dir_ = test_data_dir_.AppendASCII("api_test");
243}
244