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: 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: 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 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: 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 76 bool assertion_made() { return assertion_made_; } 77 bool failed() { return failed_; } 78 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 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: 99 StringSourceMap() {} 100 virtual ~StringSourceMap() {} 101 102 virtual v8::Handle<v8::Value> GetSource(const std::string& name) OVERRIDE { 103 if (source_map_.count(name) == 0) 104 return v8::Undefined(); 105 return v8::String::New(source_map_[name].c_str()); 106 } 107 108 virtual bool Contains(const std::string& name) OVERRIDE { 109 return source_map_.count(name); 110 } 111 112 void RegisterModule(const std::string& name, const std::string& source) { 113 CHECK_EQ(0u, source_map_.count(name)) << "Module " << name << " not found"; 114 source_map_[name] = source; 115 } 116 117 private: 118 std::map<std::string, std::string> source_map_; 119 }; 120 121 ModuleSystemTest::ModuleSystemTest() 122 : isolate_(v8::Isolate::GetCurrent()), 123 handle_scope_(isolate_), 124 context_( 125 new extensions::ChromeV8Context( 126 v8::Context::New( 127 isolate_, 128 g_v8_extension_configurator.Get().GetConfiguration()), 129 NULL, // WebFrame 130 NULL, // Extension 131 extensions::Feature::UNSPECIFIED_CONTEXT)), 132 source_map_(new StringSourceMap()), 133 should_assertions_be_made_(true) { 134 context_->v8_context()->Enter(); 135 assert_natives_ = new AssertNatives(context_.get()); 136 module_system_.reset(new ModuleSystem(context_.get(), source_map_.get())); 137 module_system_->RegisterNativeHandler("assert", scoped_ptr<NativeHandler>( 138 assert_natives_)); 139 module_system_->RegisterNativeHandler("logging", scoped_ptr<NativeHandler>( 140 new extensions::LoggingNativeHandler(context_.get()))); 141 module_system_->SetExceptionHandlerForTest( 142 scoped_ptr<ModuleSystem::ExceptionHandler>(new FailsOnException)); 143 } 144 145 ModuleSystemTest::~ModuleSystemTest() { 146 module_system_.reset(); 147 context_->v8_context()->Exit(); 148 } 149 150 void ModuleSystemTest::RegisterModule(const std::string& name, 151 const std::string& code) { 152 source_map_->RegisterModule(name, code); 153 } 154 155 void ModuleSystemTest::RegisterModule(const std::string& name, 156 int resource_id) { 157 const std::string& code = ResourceBundle::GetSharedInstance(). 158 GetRawDataResource(resource_id).as_string(); 159 source_map_->RegisterModule(name, code); 160 } 161 162 void ModuleSystemTest::OverrideNativeHandler(const std::string& name, 163 const std::string& code) { 164 RegisterModule(name, code); 165 module_system_->OverrideNativeHandlerForTest(name); 166 } 167 168 void ModuleSystemTest::RegisterTestFile(const std::string& module_name, 169 const std::string& file_name) { 170 base::FilePath test_js_file_path; 171 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_js_file_path)); 172 test_js_file_path = test_js_file_path.AppendASCII("extensions") 173 .AppendASCII(file_name); 174 std::string test_js; 175 ASSERT_TRUE(file_util::ReadFileToString(test_js_file_path, &test_js)); 176 source_map_->RegisterModule(module_name, test_js); 177 } 178 179 void ModuleSystemTest::TearDown() { 180 // All tests must assert at least once unless otherwise specified. 181 EXPECT_EQ(should_assertions_be_made_, 182 assert_natives_->assertion_made()); 183 EXPECT_FALSE(assert_natives_->failed()); 184 } 185 186 void ModuleSystemTest::ExpectNoAssertionsMade() { 187 should_assertions_be_made_ = false; 188 } 189 190 v8::Handle<v8::Object> ModuleSystemTest::CreateGlobal(const std::string& name) { 191 v8::HandleScope handle_scope; 192 v8::Handle<v8::Object> object = v8::Object::New(); 193 v8::Context::GetCurrent()->Global()->Set(v8::String::New(name.c_str()), 194 object); 195 return handle_scope.Close(object); 196 } 197