1// Copyright 2014 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 "extensions/browser/api_test_utils.h"
6
7#include "base/json/json_reader.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/values.h"
10#include "content/public/browser/browser_context.h"
11#include "content/public/test/test_utils.h"
12#include "extensions/browser/extension_function.h"
13#include "extensions/browser/extension_function_dispatcher.h"
14#include "extensions/common/extension_builder.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17using extensions::ExtensionFunctionDispatcher;
18
19namespace {
20
21class TestFunctionDispatcherDelegate
22    : public ExtensionFunctionDispatcher::Delegate {
23 public:
24  TestFunctionDispatcherDelegate() {}
25  virtual ~TestFunctionDispatcherDelegate() {}
26
27  // NULL implementation.
28 private:
29  DISALLOW_COPY_AND_ASSIGN(TestFunctionDispatcherDelegate);
30};
31
32base::Value* ParseJSON(const std::string& data) {
33  return base::JSONReader::Read(data);
34}
35
36base::ListValue* ParseList(const std::string& data) {
37  base::Value* result = ParseJSON(data);
38  base::ListValue* list = NULL;
39  result->GetAsList(&list);
40  return list;
41}
42
43// This helps us be able to wait until an UIThreadExtensionFunction calls
44// SendResponse.
45class SendResponseDelegate
46    : public UIThreadExtensionFunction::DelegateForTests {
47 public:
48  SendResponseDelegate() : should_post_quit_(false) {}
49
50  virtual ~SendResponseDelegate() {}
51
52  void set_should_post_quit(bool should_quit) {
53    should_post_quit_ = should_quit;
54  }
55
56  bool HasResponse() { return response_.get() != NULL; }
57
58  bool GetResponse() {
59    EXPECT_TRUE(HasResponse());
60    return *response_.get();
61  }
62
63  virtual void OnSendResponse(UIThreadExtensionFunction* function,
64                              bool success,
65                              bool bad_message) OVERRIDE {
66    ASSERT_FALSE(bad_message);
67    ASSERT_FALSE(HasResponse());
68    response_.reset(new bool);
69    *response_ = success;
70    if (should_post_quit_) {
71      base::MessageLoopForUI::current()->Quit();
72    }
73  }
74
75 private:
76  scoped_ptr<bool> response_;
77  bool should_post_quit_;
78};
79
80}  // namespace
81
82namespace extensions {
83
84namespace api_test_utils {
85
86base::Value* RunFunctionWithDelegateAndReturnSingleResult(
87    UIThreadExtensionFunction* function,
88    const std::string& args,
89    content::BrowserContext* context,
90    scoped_ptr<extensions::ExtensionFunctionDispatcher> dispatcher) {
91  return RunFunctionWithDelegateAndReturnSingleResult(
92      function, args, context, dispatcher.Pass(), NONE);
93}
94
95base::Value* RunFunctionWithDelegateAndReturnSingleResult(
96    UIThreadExtensionFunction* function,
97    const std::string& args,
98    content::BrowserContext* context,
99    scoped_ptr<extensions::ExtensionFunctionDispatcher> dispatcher,
100    RunFunctionFlags flags) {
101  scoped_refptr<ExtensionFunction> function_owner(function);
102  // Without a callback the function will not generate a result.
103  function->set_has_callback(true);
104  RunFunction(function, args, context, dispatcher.Pass(), flags);
105  EXPECT_TRUE(function->GetError().empty())
106      << "Unexpected error: " << function->GetError();
107  const base::Value* single_result = NULL;
108  if (function->GetResultList() != NULL &&
109      function->GetResultList()->Get(0, &single_result)) {
110    return single_result->DeepCopy();
111  }
112  return NULL;
113}
114
115base::Value* RunFunctionAndReturnSingleResult(
116    UIThreadExtensionFunction* function,
117    const std::string& args,
118    content::BrowserContext* context) {
119  return RunFunctionAndReturnSingleResult(function, args, context, NONE);
120}
121
122base::Value* RunFunctionAndReturnSingleResult(
123    UIThreadExtensionFunction* function,
124    const std::string& args,
125    content::BrowserContext* context,
126    RunFunctionFlags flags) {
127  TestFunctionDispatcherDelegate delegate;
128  scoped_ptr<ExtensionFunctionDispatcher> dispatcher(
129      new ExtensionFunctionDispatcher(context, &delegate));
130
131  return RunFunctionWithDelegateAndReturnSingleResult(
132      function, args, context, dispatcher.Pass(), flags);
133}
134
135std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function,
136                                      const std::string& args,
137                                      content::BrowserContext* context) {
138  return RunFunctionAndReturnError(function, args, context, NONE);
139}
140
141std::string RunFunctionAndReturnError(UIThreadExtensionFunction* function,
142                                      const std::string& args,
143                                      content::BrowserContext* context,
144                                      RunFunctionFlags flags) {
145  TestFunctionDispatcherDelegate delegate;
146  scoped_ptr<ExtensionFunctionDispatcher> dispatcher(
147      new ExtensionFunctionDispatcher(context, &delegate));
148  scoped_refptr<ExtensionFunction> function_owner(function);
149  // Without a callback the function will not generate a result.
150  function->set_has_callback(true);
151  RunFunction(function, args, context, dispatcher.Pass(), flags);
152  EXPECT_FALSE(function->GetResultList()) << "Did not expect a result";
153  return function->GetError();
154}
155
156bool RunFunction(UIThreadExtensionFunction* function,
157                 const std::string& args,
158                 content::BrowserContext* context) {
159  TestFunctionDispatcherDelegate delegate;
160  scoped_ptr<ExtensionFunctionDispatcher> dispatcher(
161      new ExtensionFunctionDispatcher(context, &delegate));
162  return RunFunction(function, args, context, dispatcher.Pass(), NONE);
163}
164
165bool RunFunction(UIThreadExtensionFunction* function,
166                 const std::string& args,
167                 content::BrowserContext* context,
168                 scoped_ptr<extensions::ExtensionFunctionDispatcher> dispatcher,
169                 RunFunctionFlags flags) {
170  scoped_ptr<base::ListValue> parsed_args(ParseList(args));
171  EXPECT_TRUE(parsed_args.get())
172      << "Could not parse extension function arguments: " << args;
173  return RunFunction(
174      function, parsed_args.Pass(), context, dispatcher.Pass(), flags);
175}
176
177bool RunFunction(UIThreadExtensionFunction* function,
178                 scoped_ptr<base::ListValue> args,
179                 content::BrowserContext* context,
180                 scoped_ptr<extensions::ExtensionFunctionDispatcher> dispatcher,
181                 RunFunctionFlags flags) {
182  SendResponseDelegate response_delegate;
183  function->set_test_delegate(&response_delegate);
184  function->SetArgs(args.get());
185
186  CHECK(dispatcher);
187  function->set_dispatcher(dispatcher->AsWeakPtr());
188
189  function->set_browser_context(context);
190  function->set_include_incognito(flags & INCLUDE_INCOGNITO);
191  function->Run()->Execute();
192
193  // If the RunAsync of |function| didn't already call SendResponse, run the
194  // message loop until they do.
195  if (!response_delegate.HasResponse()) {
196    response_delegate.set_should_post_quit(true);
197    content::RunMessageLoop();
198  }
199
200  EXPECT_TRUE(response_delegate.HasResponse());
201  return response_delegate.GetResponse();
202}
203
204}  // namespace api_test_utils
205}  // namespace extensions
206