extension_function_test_utils.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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.
4
5#include "chrome/browser/extensions/extension_function_test_utils.h"
6
7#include <string>
8
9#include "base/files/file_path.h"
10#include "base/json/json_reader.h"
11#include "base/values.h"
12#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/ui/browser.h"
15#include "chrome/test/base/ui_test_utils.h"
16#include "extensions/browser/api_test_utils.h"
17#include "extensions/browser/extension_function.h"
18#include "extensions/browser/extension_function_dispatcher.h"
19#include "extensions/common/extension.h"
20#include "extensions/common/id_util.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23using content::WebContents;
24using extensions::Extension;
25using extensions::Manifest;
26namespace keys = extensions::tabs_constants;
27
28namespace {
29
30class TestFunctionDispatcherDelegate
31    : public extensions::ExtensionFunctionDispatcher::Delegate {
32 public:
33  explicit TestFunctionDispatcherDelegate(Browser* browser) :
34      browser_(browser) {}
35  virtual ~TestFunctionDispatcherDelegate() {}
36
37 private:
38  virtual extensions::WindowController* GetExtensionWindowController()
39      const OVERRIDE {
40    return browser_->extension_window_controller();
41  }
42
43  virtual WebContents* GetAssociatedWebContents() const OVERRIDE {
44    return NULL;
45  }
46
47  Browser* browser_;
48};
49
50}  // namespace
51
52namespace extension_function_test_utils {
53
54base::Value* ParseJSON(const std::string& data) {
55  return base::JSONReader::Read(data);
56}
57
58base::ListValue* ParseList(const std::string& data) {
59  base::Value* result = ParseJSON(data);
60  base::ListValue* list = NULL;
61  result->GetAsList(&list);
62  return list;
63}
64
65base::DictionaryValue* ParseDictionary(
66    const std::string& data) {
67  base::Value* result = ParseJSON(data);
68  base::DictionaryValue* dict = NULL;
69  result->GetAsDictionary(&dict);
70  return dict;
71}
72
73bool GetBoolean(base::DictionaryValue* val, const std::string& key) {
74  bool result = false;
75  if (!val->GetBoolean(key, &result))
76      ADD_FAILURE() << key << " does not exist or is not a boolean.";
77  return result;
78}
79
80int GetInteger(base::DictionaryValue* val, const std::string& key) {
81  int result = 0;
82  if (!val->GetInteger(key, &result))
83    ADD_FAILURE() << key << " does not exist or is not an integer.";
84  return result;
85}
86
87std::string GetString(base::DictionaryValue* val, const std::string& key) {
88  std::string result;
89  if (!val->GetString(key, &result))
90    ADD_FAILURE() << key << " does not exist or is not a string.";
91  return result;
92}
93
94base::DictionaryValue* ToDictionary(base::Value* val) {
95  EXPECT_TRUE(val);
96  EXPECT_EQ(base::Value::TYPE_DICTIONARY, val->GetType());
97  return static_cast<base::DictionaryValue*>(val);
98}
99
100base::ListValue* ToList(base::Value* val) {
101  EXPECT_TRUE(val);
102  EXPECT_EQ(base::Value::TYPE_LIST, val->GetType());
103  return static_cast<base::ListValue*>(val);
104}
105
106scoped_refptr<Extension> CreateEmptyExtension() {
107  return CreateEmptyExtensionWithLocation(Manifest::INTERNAL);
108}
109
110scoped_refptr<Extension> CreateEmptyExtensionWithLocation(
111    Manifest::Location location) {
112  scoped_ptr<base::DictionaryValue> test_extension_value(
113      ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}"));
114  return CreateExtension(location, test_extension_value.get(), std::string());
115}
116
117scoped_refptr<Extension> CreateEmptyExtension(
118    const std::string& id_input) {
119  scoped_ptr<base::DictionaryValue> test_extension_value(
120      ParseDictionary("{\"name\": \"Test\", \"version\": \"1.0\"}"));
121  return CreateExtension(Manifest::INTERNAL, test_extension_value.get(),
122                         id_input);
123}
124
125scoped_refptr<Extension> CreateExtension(
126    base::DictionaryValue* test_extension_value) {
127  return CreateExtension(Manifest::INTERNAL, test_extension_value,
128                         std::string());
129}
130
131scoped_refptr<Extension> CreateExtension(
132    Manifest::Location location,
133    base::DictionaryValue* test_extension_value,
134    const std::string& id_input) {
135  std::string error;
136  const base::FilePath test_extension_path;
137  std::string id;
138  if (!id_input.empty())
139    id = extensions::id_util::GenerateId(id_input);
140  scoped_refptr<Extension> extension(Extension::Create(
141      test_extension_path,
142      location,
143      *test_extension_value,
144      Extension::NO_FLAGS,
145      id,
146      &error));
147  EXPECT_TRUE(error.empty()) << "Could not parse test extension " << error;
148  return extension;
149}
150
151bool HasPrivacySensitiveFields(base::DictionaryValue* val) {
152  std::string result;
153  if (val->GetString(keys::kUrlKey, &result) ||
154      val->GetString(keys::kTitleKey, &result) ||
155      val->GetString(keys::kFaviconUrlKey, &result))
156    return true;
157  return false;
158}
159
160std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function,
161                                      const std::string& args,
162                                      Browser* browser) {
163  return RunFunctionAndReturnError(function, args, browser, NONE);
164}
165std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function,
166                                      const std::string& args,
167                                      Browser* browser,
168                                      RunFunctionFlags flags) {
169  scoped_refptr<ExtensionFunction> function_owner(function);
170  // Without a callback the function will not generate a result.
171  function->set_has_callback(true);
172  RunFunction(function, args, browser, flags);
173  EXPECT_FALSE(function->GetResultList()) << "Did not expect a result";
174  return function->GetError();
175}
176
177base::Value* RunFunctionAndReturnSingleResult(
178    UIThreadExtensionFunction* function,
179    const std::string& args,
180    Browser* browser) {
181  return RunFunctionAndReturnSingleResult(function, args, browser, NONE);
182}
183base::Value* RunFunctionAndReturnSingleResult(
184    UIThreadExtensionFunction* function,
185    const std::string& args,
186    Browser* browser,
187    RunFunctionFlags flags) {
188  scoped_refptr<ExtensionFunction> function_owner(function);
189  // Without a callback the function will not generate a result.
190  function->set_has_callback(true);
191  RunFunction(function, args, browser, flags);
192  EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: "
193      << function->GetError();
194  const base::Value* single_result = NULL;
195  if (function->GetResultList() != NULL &&
196      function->GetResultList()->Get(0, &single_result)) {
197    return single_result->DeepCopy();
198  }
199  return NULL;
200}
201
202// This helps us be able to wait until an UIThreadExtensionFunction calls
203// SendResponse.
204class SendResponseDelegate
205    : public UIThreadExtensionFunction::DelegateForTests {
206 public:
207  SendResponseDelegate() : should_post_quit_(false) {}
208
209  virtual ~SendResponseDelegate() {}
210
211  void set_should_post_quit(bool should_quit) {
212    should_post_quit_ = should_quit;
213  }
214
215  bool HasResponse() {
216    return response_.get() != NULL;
217  }
218
219  bool GetResponse() {
220    EXPECT_TRUE(HasResponse());
221    return *response_.get();
222  }
223
224  virtual void OnSendResponse(UIThreadExtensionFunction* function,
225                              bool success,
226                              bool bad_message) OVERRIDE {
227    ASSERT_FALSE(bad_message);
228    ASSERT_FALSE(HasResponse());
229    response_.reset(new bool);
230    *response_ = success;
231    if (should_post_quit_) {
232      base::MessageLoopForUI::current()->Quit();
233    }
234  }
235
236 private:
237  scoped_ptr<bool> response_;
238  bool should_post_quit_;
239};
240
241bool RunFunction(UIThreadExtensionFunction* function,
242                 const std::string& args,
243                 Browser* browser,
244                 RunFunctionFlags flags) {
245  TestFunctionDispatcherDelegate dispatcher_delegate(browser);
246  scoped_ptr<extensions::ExtensionFunctionDispatcher> dispatcher(
247      new extensions::ExtensionFunctionDispatcher(browser->profile(),
248                                                  &dispatcher_delegate));
249  // TODO(yoz): The cast is a hack; these flags should be defined in
250  // only one place.  See crbug.com/394840.
251  return extensions::api_test_utils::RunFunction(
252      function,
253      args,
254      browser->profile(),
255      dispatcher.Pass(),
256      static_cast<extensions::api_test_utils::RunFunctionFlags>(flags));
257}
258
259} // namespace extension_function_test_utils
260