| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gin/modules/module_registry.h" |
| |
| #include <stdint.h> |
| |
| #include "base/bind.h" |
| #include "base/macros.h" |
| #include "gin/modules/module_registry_observer.h" |
| #include "gin/modules/module_runner_delegate.h" |
| #include "gin/public/context_holder.h" |
| #include "gin/public/isolate_holder.h" |
| #include "gin/shell_runner.h" |
| #include "gin/test/v8_test.h" |
| #include "v8/include/v8.h" |
| |
| namespace gin { |
| |
| namespace { |
| |
| struct TestHelper { |
| TestHelper(v8::Isolate* isolate) |
| : delegate(std::vector<base::FilePath>()), |
| runner(new ShellRunner(&delegate, isolate)), |
| scope(runner.get()) { |
| } |
| |
| ModuleRunnerDelegate delegate; |
| scoped_ptr<ShellRunner> runner; |
| Runner::Scope scope; |
| }; |
| |
| class ModuleRegistryObserverImpl : public ModuleRegistryObserver { |
| public: |
| ModuleRegistryObserverImpl() : did_add_count_(0) {} |
| |
| void OnDidAddPendingModule( |
| const std::string& id, |
| const std::vector<std::string>& dependencies) override { |
| did_add_count_++; |
| id_ = id; |
| dependencies_ = dependencies; |
| } |
| |
| int did_add_count() { return did_add_count_; } |
| const std::string& id() const { return id_; } |
| const std::vector<std::string>& dependencies() const { return dependencies_; } |
| |
| private: |
| int did_add_count_; |
| std::string id_; |
| std::vector<std::string> dependencies_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ModuleRegistryObserverImpl); |
| }; |
| |
| void NestedCallback(v8::Local<v8::Value> value) { |
| FAIL() << "Should not be called"; |
| } |
| |
| void OnModuleLoaded(TestHelper* helper, |
| v8::Isolate* isolate, |
| int64_t* counter, |
| v8::Local<v8::Value> value) { |
| ASSERT_TRUE(value->IsNumber()); |
| v8::Local<v8::Integer> int_value = v8::Local<v8::Integer>::Cast(value); |
| *counter += int_value->Value(); |
| ModuleRegistry::From(helper->runner->GetContextHolder()->context()) |
| ->LoadModule(isolate, "two", base::Bind(NestedCallback)); |
| } |
| |
| void OnModuleLoadedNoOp(v8::Local<v8::Value> value) { |
| ASSERT_TRUE(value->IsNumber()); |
| } |
| |
| } // namespace |
| |
| typedef V8Test ModuleRegistryTest; |
| |
| // Verifies ModuleRegistry is not available after ContextHolder has been |
| // deleted. |
| TEST_F(ModuleRegistryTest, DestroyedWithContext) { |
| v8::Isolate::Scope isolate_scope(instance_->isolate()); |
| v8::HandleScope handle_scope(instance_->isolate()); |
| v8::Local<v8::Context> context = v8::Context::New( |
| instance_->isolate(), NULL, v8::Local<v8::ObjectTemplate>()); |
| { |
| ContextHolder context_holder(instance_->isolate()); |
| context_holder.SetContext(context); |
| ModuleRegistry* registry = ModuleRegistry::From(context); |
| EXPECT_TRUE(registry != NULL); |
| } |
| ModuleRegistry* registry = ModuleRegistry::From(context); |
| EXPECT_TRUE(registry == NULL); |
| } |
| |
| // Verifies ModuleRegistryObserver is notified appropriately. |
| TEST_F(ModuleRegistryTest, ModuleRegistryObserverTest) { |
| TestHelper helper(instance_->isolate()); |
| std::string source = |
| "define('id', ['dep1', 'dep2'], function() {" |
| " return function() {};" |
| "});"; |
| |
| ModuleRegistryObserverImpl observer; |
| ModuleRegistry::From(helper.runner->GetContextHolder()->context())-> |
| AddObserver(&observer); |
| helper.runner->Run(source, "script"); |
| ModuleRegistry::From(helper.runner->GetContextHolder()->context())-> |
| RemoveObserver(&observer); |
| EXPECT_EQ(1, observer.did_add_count()); |
| EXPECT_EQ("id", observer.id()); |
| ASSERT_EQ(2u, observer.dependencies().size()); |
| EXPECT_EQ("dep1", observer.dependencies()[0]); |
| EXPECT_EQ("dep2", observer.dependencies()[1]); |
| } |
| |
| // Verifies that multiple LoadModule calls for the same module are handled |
| // correctly. |
| TEST_F(ModuleRegistryTest, LoadModuleTest) { |
| TestHelper helper(instance_->isolate()); |
| int64_t counter = 0; |
| std::string source = |
| "define('one', [], function() {" |
| " return 1;" |
| "});"; |
| |
| ModuleRegistry::LoadModuleCallback callback = |
| base::Bind(OnModuleLoaded, &helper, instance_->isolate(), &counter); |
| for (int i = 0; i < 3; i++) { |
| ModuleRegistry::From(helper.runner->GetContextHolder()->context()) |
| ->LoadModule(instance_->isolate(), "one", callback); |
| } |
| EXPECT_EQ(0, counter); |
| helper.runner->Run(source, "script"); |
| EXPECT_EQ(3, counter); |
| } |
| |
| // Verifies that explicitly loading a module that's already pending does |
| // not cause the ModuleRegistry's unsatisfied_dependency set to grow. |
| TEST_F(ModuleRegistryTest, UnsatisfiedDependenciesTest) { |
| TestHelper helper(instance_->isolate()); |
| std::string source = |
| "define('one', ['no_such_module'], function(nsm) {" |
| " return 1;" |
| "});"; |
| ModuleRegistry* registry = |
| ModuleRegistry::From(helper.runner->GetContextHolder()->context()); |
| |
| std::set<std::string> no_such_module_set; |
| no_such_module_set.insert("no_such_module"); |
| |
| // Adds one unsatisfied dependency on "no-such-module". |
| helper.runner->Run(source, "script"); |
| EXPECT_EQ(no_such_module_set, registry->unsatisfied_dependencies()); |
| |
| // Should have no effect on the unsatisfied_dependencies set. |
| ModuleRegistry::LoadModuleCallback callback = base::Bind(OnModuleLoadedNoOp); |
| registry->LoadModule(instance_->isolate(), "one", callback); |
| EXPECT_EQ(no_such_module_set, registry->unsatisfied_dependencies()); |
| } |
| |
| } // namespace gin |