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