diff options
author | sammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-26 19:48:04 +0000 |
---|---|---|
committer | sammc@chromium.org <sammc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-26 19:48:04 +0000 |
commit | f80685c31c04410dc90bc45f492f4adc73ace117 (patch) | |
tree | 10bc5ea060c8538b846ff79a44beb0ab18947904 /extensions/renderer/api_test_base.cc | |
parent | 5f2d097e8e839a4ab180cc8f63a5ddec4378e408 (diff) | |
download | chromium_src-f80685c31c04410dc90bc45f492f4adc73ace117.zip chromium_src-f80685c31c04410dc90bc45f492f4adc73ace117.tar.gz chromium_src-f80685c31c04410dc90bc45f492f4adc73ace117.tar.bz2 |
Add support for writing unit tests for Mojo-backed apps/extensions APIs.
This change adds the infrastructure for unit testing the JS parts of
apps and extensions APIs implemented on top of Mojo services. The test
environment provides a partial implementation of the JS test API
provided to browser tests. A TestServiceProvider implementation is
used to provide test implementations of the appropriate Mojo services to
the JS code.
BUG=389016
Review URL: https://codereview.chromium.org/399363002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@285792 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions/renderer/api_test_base.cc')
-rw-r--r-- | extensions/renderer/api_test_base.cc | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/extensions/renderer/api_test_base.cc b/extensions/renderer/api_test_base.cc new file mode 100644 index 0000000..f0943c9 --- /dev/null +++ b/extensions/renderer/api_test_base.cc @@ -0,0 +1,221 @@ +// 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 "extensions/renderer/api_test_base.h" + +#include <vector> + +#include "base/run_loop.h" +#include "extensions/common/extension_urls.h" +#include "extensions/renderer/dispatcher.h" +#include "extensions/renderer/process_info_native_handler.h" +#include "gin/converter.h" +#include "gin/dictionary.h" +#include "mojo/bindings/js/core.h" +#include "mojo/bindings/js/handle.h" +#include "mojo/bindings/js/support.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/system/core.h" + +namespace extensions { +namespace { + +// Natives for the implementation of the unit test version of chrome.test. Calls +// the provided |quit_closure| when either notifyPass or notifyFail is called. +class TestNatives : public gin::Wrappable<TestNatives> { + public: + static gin::Handle<TestNatives> Create(v8::Isolate* isolate, + const base::Closure& quit_closure) { + return gin::CreateHandle(isolate, new TestNatives(quit_closure)); + } + + virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) OVERRIDE { + return Wrappable<TestNatives>::GetObjectTemplateBuilder(isolate) + .SetMethod("Log", &TestNatives::Log) + .SetMethod("NotifyPass", &TestNatives::NotifyPass) + .SetMethod("NotifyFail", &TestNatives::NotifyFail); + } + + void Log(const std::string& value) { logs_ += value + "\n"; } + void NotifyPass() { FinishTesting(); } + + void NotifyFail(const std::string& message) { + FinishTesting(); + FAIL() << logs_ << message; + } + + void FinishTesting() { + base::MessageLoop::current()->PostTask(FROM_HERE, quit_closure_); + } + + static gin::WrapperInfo kWrapperInfo; + + private: + explicit TestNatives(const base::Closure& quit_closure) + : quit_closure_(quit_closure) {} + + const base::Closure quit_closure_; + std::string logs_; +}; + +gin::WrapperInfo TestNatives::kWrapperInfo = {gin::kEmbedderNativeGin}; + +} // namespace + +gin::WrapperInfo TestServiceProvider::kWrapperInfo = {gin::kEmbedderNativeGin}; + +gin::Handle<TestServiceProvider> TestServiceProvider::Create( + v8::Isolate* isolate) { + return gin::CreateHandle(isolate, new TestServiceProvider()); +} + +TestServiceProvider::~TestServiceProvider() { +} + +gin::ObjectTemplateBuilder TestServiceProvider::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return Wrappable<TestServiceProvider>::GetObjectTemplateBuilder(isolate) + .SetMethod("connectToService", &TestServiceProvider::ConnectToService); +} + +mojo::Handle TestServiceProvider::ConnectToService( + const std::string& service_name) { + EXPECT_EQ(1u, service_factories_.count(service_name)) + << "Unregistered service " << service_name << " requested."; + mojo::MessagePipe pipe; + std::map<std::string, + base::Callback<void(mojo::ScopedMessagePipeHandle)> >::iterator it = + service_factories_.find(service_name); + if (it != service_factories_.end()) + it->second.Run(pipe.handle0.Pass()); + return pipe.handle1.release(); +} + +TestServiceProvider::TestServiceProvider() { +} + +ApiTestBase::ApiTestBase() { +} +ApiTestBase::~ApiTestBase() { +} + +void ApiTestBase::SetUp() { + ModuleSystemTest::SetUp(); + InitializeEnvironment(); + RegisterModules(); +} + +void ApiTestBase::RegisterModules() { + v8_schema_registry_.reset(new V8SchemaRegistry); + const std::vector<std::pair<std::string, int> > resources = + Dispatcher::GetJsResources(); + for (std::vector<std::pair<std::string, int> >::const_iterator resource = + resources.begin(); + resource != resources.end(); + ++resource) { + if (resource->first != "test_environment_specific_bindings") + env()->RegisterModule(resource->first, resource->second); + } + Dispatcher::RegisterNativeHandlers(env()->module_system(), + env()->context(), + NULL, + NULL, + v8_schema_registry_.get()); + env()->module_system()->RegisterNativeHandler( + "process", + scoped_ptr<NativeHandler>(new ProcessInfoNativeHandler( + env()->context(), + env()->context()->GetExtensionID(), + env()->context()->GetContextTypeDescription(), + false, + 2, + false))); + env()->RegisterTestFile("test_environment_specific_bindings", + "unit_test_environment_specific_bindings.js"); + + env()->OverrideNativeHandler("activityLogger", + "exports.LogAPICall = function() {};"); + env()->OverrideNativeHandler( + "apiDefinitions", + "exports.GetExtensionAPIDefinitionsForTest = function() { return [] };"); + env()->OverrideNativeHandler( + "event_natives", + "exports.AttachEvent = function() {};" + "exports.DetachEvent = function() {};" + "exports.AttachFilteredEvent = function() {};" + "exports.AttachFilteredEvent = function() {};" + "exports.MatchAgainstEventFilter = function() { return [] };"); + + gin::ModuleRegistry::From(env()->context()->v8_context()) + ->AddBuiltinModule(env()->isolate(), + mojo::js::Core::kModuleName, + mojo::js::Core::GetModule(env()->isolate())); + gin::ModuleRegistry::From(env()->context()->v8_context()) + ->AddBuiltinModule(env()->isolate(), + mojo::js::Support::kModuleName, + mojo::js::Support::GetModule(env()->isolate())); + gin::Handle<TestServiceProvider> service_provider = + TestServiceProvider::Create(env()->isolate()); + service_provider_ = service_provider.get(); + gin::ModuleRegistry::From(env()->context()->v8_context()) + ->AddBuiltinModule(env()->isolate(), + "content/public/renderer/service_provider", + service_provider.ToV8()); +} + +void ApiTestBase::InitializeEnvironment() { + gin::Dictionary global(env()->isolate(), + env()->context()->v8_context()->Global()); + gin::Dictionary navigator(gin::Dictionary::CreateEmpty(env()->isolate())); + navigator.Set("appVersion", base::StringPiece("")); + global.Set("navigator", navigator); + gin::Dictionary chrome(gin::Dictionary::CreateEmpty(env()->isolate())); + global.Set("chrome", chrome); + gin::Dictionary extension(gin::Dictionary::CreateEmpty(env()->isolate())); + chrome.Set("extension", extension); + gin::Dictionary runtime(gin::Dictionary::CreateEmpty(env()->isolate())); + chrome.Set("runtime", runtime); +} + +void ApiTestBase::RunTest(const std::string& file_name, + const std::string& test_name) { + env()->RegisterTestFile("testBody", file_name); + ExpectNoAssertionsMade(); + base::RunLoop run_loop; + gin::ModuleRegistry::From(env()->context()->v8_context())->AddBuiltinModule( + env()->isolate(), + "testNatives", + TestNatives::Create(env()->isolate(), run_loop.QuitClosure()).ToV8()); + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&ApiTestBase::RunTestInner, + base::Unretained(this), + test_name, + run_loop.QuitClosure())); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&ApiTestBase::RunPromisesAgain, base::Unretained(this))); + run_loop.Run(); +} + +void ApiTestBase::RunTestInner(const std::string& test_name, + const base::Closure& quit_closure) { + v8::HandleScope scope(env()->isolate()); + ModuleSystem::NativesEnabledScope natives_enabled(env()->module_system()); + v8::Handle<v8::Value> result = + env()->module_system()->CallModuleMethod("testBody", test_name); + if (!result->IsTrue()) { + base::MessageLoop::current()->PostTask(FROM_HERE, quit_closure); + FAIL() << "Failed to run test \"" << test_name << "\""; + } +} + +void ApiTestBase::RunPromisesAgain() { + RunResolvedPromises(); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&ApiTestBase::RunPromisesAgain, base::Unretained(this))); +} + +} // namespace extensions |