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/test/base/module_system_test.h"
6
7 #include "base/callback.h"
8 #include "base/file_util.h"
9 #include "base/files/file_path.h"
10 #include "base/lazy_instance.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/path_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_piece.h"
15 #include "chrome/common/chrome_paths.h"
16 #include "chrome/renderer/extensions/chrome_v8_context.h"
17 #include "extensions/renderer/logging_native_handler.h"
18 #include "extensions/renderer/object_backed_native_handler.h"
19 #include "extensions/renderer/safe_builtins.h"
20 #include "extensions/renderer/utils_native_handler.h"
21 #include "ui/base/resource/resource_bundle.h"
22
23 #include <map>
24 #include <string>
25
26 using extensions::ModuleSystem;
27 using extensions::NativeHandler;
28 using extensions::ObjectBackedNativeHandler;
29
30 namespace {
31
32 class FailsOnException : public ModuleSystem::ExceptionHandler {
33 public:
HandleUncaughtException(const v8::TryCatch & try_catch)34 virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE {
35 FAIL() << "Uncaught exception: " << CreateExceptionString(try_catch);
36 }
37 };
38
39 class V8ExtensionConfigurator {
40 public:
V8ExtensionConfigurator()41 V8ExtensionConfigurator()
42 : safe_builtins_(extensions::SafeBuiltins::CreateV8Extension()),
43 names_(1, safe_builtins_->name()),
44 configuration_(new v8::ExtensionConfiguration(
45 names_.size(), vector_as_array(&names_))) {
46 v8::RegisterExtension(safe_builtins_.get());
47 }
48
GetConfiguration()49 v8::ExtensionConfiguration* GetConfiguration() {
50 return configuration_.get();
51 }
52
53 private:
54 scoped_ptr<v8::Extension> safe_builtins_;
55 std::vector<const char*> names_;
56 scoped_ptr<v8::ExtensionConfiguration> configuration_;
57 };
58
59 base::LazyInstance<V8ExtensionConfigurator>::Leaky g_v8_extension_configurator =
60 LAZY_INSTANCE_INITIALIZER;
61
62 } // namespace
63
64 // Native JS functions for doing asserts.
65 class ModuleSystemTest::AssertNatives : public ObjectBackedNativeHandler {
66 public:
AssertNatives(extensions::ChromeV8Context * context)67 explicit AssertNatives(extensions::ChromeV8Context* context)
68 : ObjectBackedNativeHandler(context),
69 assertion_made_(false),
70 failed_(false) {
71 RouteFunction("AssertTrue", base::Bind(&AssertNatives::AssertTrue,
72 base::Unretained(this)));
73 RouteFunction("AssertFalse", base::Bind(&AssertNatives::AssertFalse,
74 base::Unretained(this)));
75 }
76
assertion_made()77 bool assertion_made() { return assertion_made_; }
failed()78 bool failed() { return failed_; }
79
AssertTrue(const v8::FunctionCallbackInfo<v8::Value> & args)80 void AssertTrue(const v8::FunctionCallbackInfo<v8::Value>& args) {
81 CHECK_EQ(1, args.Length());
82 assertion_made_ = true;
83 failed_ = failed_ || !args[0]->ToBoolean()->Value();
84 }
85
AssertFalse(const v8::FunctionCallbackInfo<v8::Value> & args)86 void AssertFalse(const v8::FunctionCallbackInfo<v8::Value>& args) {
87 CHECK_EQ(1, args.Length());
88 assertion_made_ = true;
89 failed_ = failed_ || args[0]->ToBoolean()->Value();
90 }
91
92 private:
93 bool assertion_made_;
94 bool failed_;
95 };
96
97 // Source map that operates on std::strings.
98 class ModuleSystemTest::StringSourceMap : public ModuleSystem::SourceMap {
99 public:
StringSourceMap()100 StringSourceMap() {}
~StringSourceMap()101 virtual ~StringSourceMap() {}
102
GetSource(v8::Isolate * isolate,const std::string & name)103 virtual v8::Handle<v8::Value> GetSource(v8::Isolate* isolate,
104 const std::string& name) OVERRIDE {
105 if (source_map_.count(name) == 0)
106 return v8::Undefined(isolate);
107 return v8::String::NewFromUtf8(isolate, source_map_[name].c_str());
108 }
109
Contains(const std::string & name)110 virtual bool Contains(const std::string& name) OVERRIDE {
111 return source_map_.count(name);
112 }
113
RegisterModule(const std::string & name,const std::string & source)114 void RegisterModule(const std::string& name, const std::string& source) {
115 CHECK_EQ(0u, source_map_.count(name)) << "Module " << name << " not found";
116 source_map_[name] = source;
117 }
118
119 private:
120 std::map<std::string, std::string> source_map_;
121 };
122
ModuleSystemTest()123 ModuleSystemTest::ModuleSystemTest()
124 : isolate_(v8::Isolate::GetCurrent()),
125 handle_scope_(isolate_),
126 context_(
127 new extensions::ChromeV8Context(
128 v8::Context::New(
129 isolate_,
130 g_v8_extension_configurator.Get().GetConfiguration()),
131 NULL, // WebFrame
132 NULL, // Extension
133 extensions::Feature::UNSPECIFIED_CONTEXT)),
134 source_map_(new StringSourceMap()),
135 should_assertions_be_made_(true) {
136 context_->v8_context()->Enter();
137 assert_natives_ = new AssertNatives(context_.get());
138
139 {
140 scoped_ptr<ModuleSystem> module_system(
141 new ModuleSystem(context_.get(), source_map_.get()));
142 context_->set_module_system(module_system.Pass());
143 }
144 ModuleSystem* module_system = context_->module_system();
145 module_system->RegisterNativeHandler("assert", scoped_ptr<NativeHandler>(
146 assert_natives_));
147 module_system->RegisterNativeHandler("logging", scoped_ptr<NativeHandler>(
148 new extensions::LoggingNativeHandler(context_.get())));
149 module_system->RegisterNativeHandler("utils", scoped_ptr<NativeHandler>(
150 new extensions::UtilsNativeHandler(context_.get())));
151 module_system->SetExceptionHandlerForTest(
152 scoped_ptr<ModuleSystem::ExceptionHandler>(new FailsOnException));
153 }
154
~ModuleSystemTest()155 ModuleSystemTest::~ModuleSystemTest() {
156 context_->v8_context()->Exit();
157 }
158
RegisterModule(const std::string & name,const std::string & code)159 void ModuleSystemTest::RegisterModule(const std::string& name,
160 const std::string& code) {
161 source_map_->RegisterModule(name, code);
162 }
163
RegisterModule(const std::string & name,int resource_id)164 void ModuleSystemTest::RegisterModule(const std::string& name,
165 int resource_id) {
166 const std::string& code = ResourceBundle::GetSharedInstance().
167 GetRawDataResource(resource_id).as_string();
168 source_map_->RegisterModule(name, code);
169 }
170
OverrideNativeHandler(const std::string & name,const std::string & code)171 void ModuleSystemTest::OverrideNativeHandler(const std::string& name,
172 const std::string& code) {
173 RegisterModule(name, code);
174 context_->module_system()->OverrideNativeHandlerForTest(name);
175 }
176
RegisterTestFile(const std::string & module_name,const std::string & file_name)177 void ModuleSystemTest::RegisterTestFile(const std::string& module_name,
178 const std::string& file_name) {
179 base::FilePath test_js_file_path;
180 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_js_file_path));
181 test_js_file_path = test_js_file_path.AppendASCII("extensions")
182 .AppendASCII(file_name);
183 std::string test_js;
184 ASSERT_TRUE(base::ReadFileToString(test_js_file_path, &test_js));
185 source_map_->RegisterModule(module_name, test_js);
186 }
187
TearDown()188 void ModuleSystemTest::TearDown() {
189 // All tests must assert at least once unless otherwise specified.
190 EXPECT_EQ(should_assertions_be_made_,
191 assert_natives_->assertion_made());
192 EXPECT_FALSE(assert_natives_->failed());
193 }
194
ExpectNoAssertionsMade()195 void ModuleSystemTest::ExpectNoAssertionsMade() {
196 should_assertions_be_made_ = false;
197 }
198
CreateGlobal(const std::string & name)199 v8::Handle<v8::Object> ModuleSystemTest::CreateGlobal(const std::string& name) {
200 v8::Isolate* isolate = v8::Isolate::GetCurrent();
201 v8::EscapableHandleScope handle_scope(isolate);
202 v8::Local<v8::Object> object = v8::Object::New(isolate);
203 isolate->GetCurrentContext()->Global()->Set(
204 v8::String::NewFromUtf8(isolate, name.c_str()), object);
205 return handle_scope.Escape(object);
206 }
207