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/renderer/api_test_base.h"
6
7#include <vector>
8
9#include "base/run_loop.h"
10#include "extensions/common/extension_urls.h"
11#include "extensions/renderer/dispatcher.h"
12#include "extensions/renderer/process_info_native_handler.h"
13#include "gin/converter.h"
14#include "gin/dictionary.h"
15#include "mojo/bindings/js/core.h"
16#include "mojo/bindings/js/handle.h"
17#include "mojo/bindings/js/support.h"
18#include "mojo/public/cpp/bindings/interface_request.h"
19#include "mojo/public/cpp/system/core.h"
20
21namespace extensions {
22namespace {
23
24// Natives for the implementation of the unit test version of chrome.test. Calls
25// the provided |quit_closure| when either notifyPass or notifyFail is called.
26class TestNatives : public gin::Wrappable<TestNatives> {
27 public:
28  static gin::Handle<TestNatives> Create(v8::Isolate* isolate,
29                                         const base::Closure& quit_closure) {
30    return gin::CreateHandle(isolate, new TestNatives(quit_closure));
31  }
32
33  virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
34      v8::Isolate* isolate) OVERRIDE {
35    return Wrappable<TestNatives>::GetObjectTemplateBuilder(isolate)
36        .SetMethod("Log", &TestNatives::Log)
37        .SetMethod("NotifyPass", &TestNatives::NotifyPass)
38        .SetMethod("NotifyFail", &TestNatives::NotifyFail);
39  }
40
41  void Log(const std::string& value) { logs_ += value + "\n"; }
42  void NotifyPass() { FinishTesting(); }
43
44  void NotifyFail(const std::string& message) {
45    FinishTesting();
46    FAIL() << logs_ << message;
47  }
48
49  void FinishTesting() {
50    base::MessageLoop::current()->PostTask(FROM_HERE, quit_closure_);
51  }
52
53  static gin::WrapperInfo kWrapperInfo;
54
55 private:
56  explicit TestNatives(const base::Closure& quit_closure)
57      : quit_closure_(quit_closure) {}
58
59  const base::Closure quit_closure_;
60  std::string logs_;
61};
62
63gin::WrapperInfo TestNatives::kWrapperInfo = {gin::kEmbedderNativeGin};
64
65}  // namespace
66
67gin::WrapperInfo TestServiceProvider::kWrapperInfo = {gin::kEmbedderNativeGin};
68
69gin::Handle<TestServiceProvider> TestServiceProvider::Create(
70    v8::Isolate* isolate) {
71  return gin::CreateHandle(isolate, new TestServiceProvider());
72}
73
74TestServiceProvider::~TestServiceProvider() {
75}
76
77gin::ObjectTemplateBuilder TestServiceProvider::GetObjectTemplateBuilder(
78    v8::Isolate* isolate) {
79  return Wrappable<TestServiceProvider>::GetObjectTemplateBuilder(isolate)
80      .SetMethod("connectToService", &TestServiceProvider::ConnectToService);
81}
82
83mojo::Handle TestServiceProvider::ConnectToService(
84    const std::string& service_name) {
85  EXPECT_EQ(1u, service_factories_.count(service_name))
86      << "Unregistered service " << service_name << " requested.";
87  mojo::MessagePipe pipe;
88  std::map<std::string,
89           base::Callback<void(mojo::ScopedMessagePipeHandle)> >::iterator it =
90      service_factories_.find(service_name);
91  if (it != service_factories_.end())
92    it->second.Run(pipe.handle0.Pass());
93  return pipe.handle1.release();
94}
95
96TestServiceProvider::TestServiceProvider() {
97}
98
99ApiTestBase::ApiTestBase() {
100}
101ApiTestBase::~ApiTestBase() {
102}
103
104void ApiTestBase::SetUp() {
105  ModuleSystemTest::SetUp();
106  InitializeEnvironment();
107  RegisterModules();
108}
109
110void ApiTestBase::RegisterModules() {
111  v8_schema_registry_.reset(new V8SchemaRegistry);
112  const std::vector<std::pair<std::string, int> > resources =
113      Dispatcher::GetJsResources();
114  for (std::vector<std::pair<std::string, int> >::const_iterator resource =
115           resources.begin();
116       resource != resources.end();
117       ++resource) {
118    if (resource->first != "test_environment_specific_bindings")
119      env()->RegisterModule(resource->first, resource->second);
120  }
121  Dispatcher::RegisterNativeHandlers(env()->module_system(),
122                                     env()->context(),
123                                     NULL,
124                                     NULL,
125                                     v8_schema_registry_.get());
126  env()->module_system()->RegisterNativeHandler(
127      "process",
128      scoped_ptr<NativeHandler>(new ProcessInfoNativeHandler(
129          env()->context(),
130          env()->context()->GetExtensionID(),
131          env()->context()->GetContextTypeDescription(),
132          false,
133          2,
134          false)));
135  env()->RegisterTestFile("test_environment_specific_bindings",
136                          "unit_test_environment_specific_bindings.js");
137
138  env()->OverrideNativeHandler("activityLogger",
139                               "exports.LogAPICall = function() {};");
140  env()->OverrideNativeHandler(
141      "apiDefinitions",
142      "exports.GetExtensionAPIDefinitionsForTest = function() { return [] };");
143  env()->OverrideNativeHandler(
144      "event_natives",
145      "exports.AttachEvent = function() {};"
146      "exports.DetachEvent = function() {};"
147      "exports.AttachFilteredEvent = function() {};"
148      "exports.AttachFilteredEvent = function() {};"
149      "exports.MatchAgainstEventFilter = function() { return [] };");
150
151  gin::ModuleRegistry::From(env()->context()->v8_context())
152      ->AddBuiltinModule(env()->isolate(),
153                         mojo::js::Core::kModuleName,
154                         mojo::js::Core::GetModule(env()->isolate()));
155  gin::ModuleRegistry::From(env()->context()->v8_context())
156      ->AddBuiltinModule(env()->isolate(),
157                         mojo::js::Support::kModuleName,
158                         mojo::js::Support::GetModule(env()->isolate()));
159  gin::Handle<TestServiceProvider> service_provider =
160      TestServiceProvider::Create(env()->isolate());
161  service_provider_ = service_provider.get();
162  gin::ModuleRegistry::From(env()->context()->v8_context())
163      ->AddBuiltinModule(env()->isolate(),
164                         "content/public/renderer/service_provider",
165                         service_provider.ToV8());
166}
167
168void ApiTestBase::InitializeEnvironment() {
169  gin::Dictionary global(env()->isolate(),
170                         env()->context()->v8_context()->Global());
171  gin::Dictionary navigator(gin::Dictionary::CreateEmpty(env()->isolate()));
172  navigator.Set("appVersion", base::StringPiece(""));
173  global.Set("navigator", navigator);
174  gin::Dictionary chrome(gin::Dictionary::CreateEmpty(env()->isolate()));
175  global.Set("chrome", chrome);
176  gin::Dictionary extension(gin::Dictionary::CreateEmpty(env()->isolate()));
177  chrome.Set("extension", extension);
178  gin::Dictionary runtime(gin::Dictionary::CreateEmpty(env()->isolate()));
179  chrome.Set("runtime", runtime);
180}
181
182void ApiTestBase::RunTest(const std::string& file_name,
183                          const std::string& test_name) {
184  env()->RegisterTestFile("testBody", file_name);
185  ExpectNoAssertionsMade();
186  base::RunLoop run_loop;
187  gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule(
188      env()->isolate(),
189      "testNatives",
190      TestNatives::Create(env()->isolate(), run_loop.QuitClosure()).ToV8());
191  base::MessageLoop::current()->PostTask(FROM_HERE,
192                                         base::Bind(&ApiTestBase::RunTestInner,
193                                                    base::Unretained(this),
194                                                    test_name,
195                                                    run_loop.QuitClosure()));
196  base::MessageLoop::current()->PostTask(
197      FROM_HERE,
198      base::Bind(&ApiTestBase::RunPromisesAgain, base::Unretained(this)));
199  run_loop.Run();
200}
201
202void ApiTestBase::RunTestInner(const std::string& test_name,
203                               const base::Closure& quit_closure) {
204  v8::HandleScope scope(env()->isolate());
205  ModuleSystem::NativesEnabledScope natives_enabled(env()->module_system());
206  v8::Handle<v8::Value> result =
207      env()->module_system()->CallModuleMethod("testBody", test_name);
208  if (!result->IsTrue()) {
209    base::MessageLoop::current()->PostTask(FROM_HERE, quit_closure);
210    FAIL() << "Failed to run test \"" << test_name << "\"";
211  }
212}
213
214void ApiTestBase::RunPromisesAgain() {
215  RunResolvedPromises();
216  base::MessageLoop::current()->PostTask(
217      FROM_HERE,
218      base::Bind(&ApiTestBase::RunPromisesAgain, base::Unretained(this)));
219}
220
221}  // namespace extensions
222