Commit dc9e9709 authored by eugenebut's avatar eugenebut Committed by Commit bot

[ios Mojo] Integration test for Mojo WebUI.

Tests communication between WebUI page and the native code.
When page is loaded it sends "syn" message to the native code,
when native code receives "syn" it replies back with "ack",
when page receives "ack" it replies back with "fin". Once "fin" is
received the test succeeds.

BUG=567809

Review-Url: https://codereview.chromium.org/2006273005
Cr-Commit-Position: refs/heads/master@{#396972}
parent 72b35864
...@@ -473,8 +473,13 @@ test("ios_web_inttests") { ...@@ -473,8 +473,13 @@ test("ios_web_inttests") {
":test_support", ":test_support",
":web", ":web",
"//base/test:test_support", "//base/test:test_support",
"//ios/public/provider/web",
"//ios/web/test:mojo_bindings",
"//ios/web/test:packed_resources",
"//ios/web/test:resources",
"//mojo/edk/system", "//mojo/edk/system",
"//net:test_support", "//net:test_support",
"//services/shell/public/cpp",
"//testing/gtest", "//testing/gtest",
"//ui/base:test_support", "//ui/base:test_support",
] ]
...@@ -482,6 +487,7 @@ test("ios_web_inttests") { ...@@ -482,6 +487,7 @@ test("ios_web_inttests") {
"browser_state_web_view_partition_inttest.mm", "browser_state_web_view_partition_inttest.mm",
"public/test/http_server_inttest.mm", "public/test/http_server_inttest.mm",
"test/run_all_unittests.cc", "test/run_all_unittests.cc",
"webui/web_ui_mojo_inttest.mm",
] ]
} }
...@@ -535,8 +541,4 @@ grit("resources") { ...@@ -535,8 +541,4 @@ grit("resources") {
"grit/ios_web_resources.h", "grit/ios_web_resources.h",
"ios_web_resources.pak", "ios_web_resources.pak",
] ]
grit_flags = [
"-E",
"root_out_dir=" + rebase_path(root_out_dir, root_build_dir),
]
} }
...@@ -12,16 +12,98 @@ ...@@ -12,16 +12,98 @@
'dependencies': [ 'dependencies': [
'../../base/base.gyp:base', '../../base/base.gyp:base',
'../../base/base.gyp:test_support_base', '../../base/base.gyp:test_support_base',
'../../ios/provider/ios_provider_web.gyp:ios_provider_web',
'../../net/net.gyp:net_test_support', '../../net/net.gyp:net_test_support',
'../../services/shell/shell_public.gyp:shell_public',
'../../testing/gtest.gyp:gtest', '../../testing/gtest.gyp:gtest',
'../../ui/base/ui_base.gyp:ui_base_test_support', '../../ui/base/ui_base.gyp:ui_base_test_support',
'ios_web.gyp:ios_web', 'ios_web.gyp:ios_web',
'ios_web.gyp:ios_web_test_support', 'ios_web.gyp:ios_web_test_support',
'mojo_bindings',
'test_resources',
'packed_test_resources',
], ],
'sources': [ 'sources': [
'browser_state_web_view_partition_inttest.mm', 'browser_state_web_view_partition_inttest.mm',
'public/test/http_server_inttest.mm', 'public/test/http_server_inttest.mm',
'test/run_all_unittests.cc', 'test/run_all_unittests.cc',
'webui/web_ui_mojo_inttest.mm',
],
'mac_bundle_resources': [
'<(SHARED_INTERMEDIATE_DIR)/ios/web/test/resources.pak'
],
},
{
# GN version: //ios/web/test:mojo_bindings
'target_name': 'mojo_bindings',
# The type of this target must be none. This is so that resources can
# depend upon this target for generating the js bindings files. Any
# generated cpp files be listed explicitly in browser_ui.
'type': 'none',
'sources': [
'test/mojo_test.mojom',
],
'includes': [ '../../mojo/mojom_bindings_generator.gypi' ],
},
{
# GN version: //ios/web/test:resources
'target_name': 'test_resources',
'type': 'none',
'hard_dependency': 1,
'dependencies': [
'mojo_bindings',
],
'actions': [
{
'action_name': 'test_resources',
'variables': {
'grit_grd_file': 'test/test_resources.grd',
'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/ios/web/test',
'grit_additional_defines': [
'-E', 'root_gen_dir=<(SHARED_INTERMEDIATE_DIR)',
],
},
'includes': [ '../../build/grit_action.gypi' ],
},
],
'copies': [
{
'destination': '<(PRODUCT_DIR)',
'files': [
'<(SHARED_INTERMEDIATE_DIR)/ios/web/test/test_resources.pak'
],
},
],
'direct_dependent_settings': {
'include_dirs': [
'<(SHARED_INTERMEDIATE_DIR)',
],
},
},
{
'target_name': 'packed_test_resources',
'type': 'none',
'hard_depency': 1,
'dependencies': [
'test_resources',
],
'variables': {
'repack_path': [
'../../tools/grit/grit/format/repack.py',
],
},
'actions': [
{
'action_name': 'repack_test_resources',
'variables': {
'pak_inputs': [
'<(SHARED_INTERMEDIATE_DIR)/ios/web/ios_web_resources.pak',
'<(SHARED_INTERMEDIATE_DIR)/ios/web/test/test_resources.pak'
],
'pak_output': '<(SHARED_INTERMEDIATE_DIR)/ios/web/test/resources.pak',
},
'includes': [ '../../build/repack_action.gypi' ],
},
], ],
}, },
], ],
......
...@@ -3,9 +3,44 @@ ...@@ -3,9 +3,44 @@
# found in the LICENSE file. # found in the LICENSE file.
import("//mojo/public/tools/bindings/mojom.gni") import("//mojo/public/tools/bindings/mojom.gni")
import("//tools/grit/grit_rule.gni")
import("//tools/grit/repack.gni")
mojom("mojo_bindings") { mojom("mojo_bindings") {
sources = [ sources = [
"mojo_test.mojom", "mojo_test.mojom",
] ]
} }
repack_and_bundle("packed_resources") {
testonly = true
sources = [
"$root_gen_dir/ios/web/ios_web_resources.pak",
"$root_gen_dir/ios/web/test/test_resources.pak",
]
deps = [
":resources",
"//ios/web:resources",
]
output = "$target_gen_dir/resources.pak"
bundle_output = "{{bundle_resources_dir}}/{{source_file_part}}"
}
grit("resources") {
source = "test_resources.grd"
use_qualified_include = true
inputs = [
"${root_gen_dir}/ios/web/test/mojo_test.mojom.js",
]
deps = [
":mojo_bindings",
]
outputs = [
"grit/test_resources.h",
"test_resources.pak",
]
grit_flags = [
"-E",
"root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
]
}
<!doctype html>
<html>
<head>
<script src="chrome://resources/js/ios/web_ui.js"></script>
<script src="mojo_test.js"></script>
</head>
</html>
// Copyright 2016 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.
// This module provides the test page for WebUIMojoTest. Once the page is
// loaded it sends "syn" message to the native code. Once page receives "ack"
// from the native code, the page then sends "fin". Test succeeds only when
// "fin" is received by the native page. Refer to
// ios/web/webui/web_ui_mojo_inttest.mm for testing code.
define('main', [
'mojo/public/js/bindings',
'mojo/public/js/core',
'mojo/public/js/connection',
'ios/web/test/mojo_test.mojom',
'content/public/renderer/service_provider',
], function(bindings, core, connection, browser, serviceProvider) {
var page;
function TestPageImpl(browser) {
this.browser_ = browser;
};
TestPageImpl.prototype = Object.create(browser.TestPage.stubClass.prototype);
/**
* Sends message as a string to the native code.
*
* @param {string} message Message to send.
*/
TestPageImpl.prototype.sendMessage = function(message) {
var pipe = core.createMessagePipe();
var stub = connection.bindHandleToStub(pipe.handle0, browser.TestPage);
bindings.StubBindings(stub).delegate = page;
page.stub_ = stub;
this.browser_.handleJsMessage(message, pipe.handle1);
};
/**
* Called by native code with "ack" message.
*
* @param {!NativeMessageResultMojo} result Object received from the native
code.
*/
TestPageImpl.prototype.handleNativeMessage = function(result) {
if (result.message == 'ack') {
// Native code has replied with "ack", send "fin" to complete the test.
this.sendMessage('fin');
}
};
return function() {
var browserProxy = connection.bindHandleToProxy(
serviceProvider.connectToService(browser.TestUIHandlerMojo.name),
browser.TestUIHandlerMojo);
page = new TestPageImpl(browserProxy);
// Send "syn" so native code should reply with "ack".
page.sendMessage('syn');
};
});
<?xml version="1.0" encoding="utf-8"?>
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/test_resources.h" type="rc_header">
<emit emit_type='prepend'></emit>
</output>
<output filename="test_resources.pak" type="data_package" />
</outputs>
<release seq="1">
<includes>
<include name="IDR_MOJO_TEST_HTML" file="data/mojo_test.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
<include name="IDR_MOJO_TEST_JS" file="data/mojo_test.js" type="BINDATA" />
<include name="IDR_MOJO_TEST_MOJO_JS" file="${root_gen_dir}/ios/web/test/mojo_test.mojom.js" use_base_dir="false" type="BINDATA"/>
</includes>
</release>
</grit>
...@@ -7,10 +7,12 @@ ...@@ -7,10 +7,12 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/metrics/statistics_recorder.h" #include "base/metrics/statistics_recorder.h"
#include "base/path_service.h"
#import "ios/web/public/test/test_web_client.h" #import "ios/web/public/test/test_web_client.h"
#include "ios/web/public/url_schemes.h" #include "ios/web/public/url_schemes.h"
#include "ios/web/web_thread_impl.h" #include "ios/web/web_thread_impl.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h"
namespace web { namespace web {
...@@ -43,6 +45,13 @@ void WebTestSuite::Initialize() { ...@@ -43,6 +45,13 @@ void WebTestSuite::Initialize() {
new WebTestSuiteListener); new WebTestSuiteListener);
RegisterWebSchemes(false); RegisterWebSchemes(false);
// Load test resources if they are present in the bundle.
base::FilePath resources_pack_path;
base::PathService::Get(base::DIR_MODULE, &resources_pack_path);
resources_pack_path =
resources_pack_path.Append(FILE_PATH_LITERAL("resources.pak"));
ui::ResourceBundle::InitSharedInstanceWithPakPath(resources_pack_path);
} }
} // namespace web } // namespace web
// Copyright 2016 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 <memory>
#include "base/run_loop.h"
#import "base/test/ios/wait_util.h"
#include "ios/public/provider/web/web_ui_ios_controller.h"
#include "ios/public/provider/web/web_ui_ios_controller_factory.h"
#import "ios/web/public/navigation_manager.h"
#include "ios/web/public/web_ui_ios_data_source.h"
#include "ios/web/test/grit/test_resources.h"
#include "ios/web/test/mojo_test.mojom.h"
#include "ios/web/test/test_url_constants.h"
#import "ios/web/test/web_int_test.h"
#import "ios/web/web_state/web_state_impl.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "services/shell/public/cpp/interface_registry.h"
#include "url/gurl.h"
#include "url/scheme_host_port.h"
namespace web {
namespace {
// Hostname for test WebUI page.
const char kTestWebUIURLHost[] = "testwebui";
// UI handler class which communicates with test WebUI page as follows:
// - page sends "syn" message to |TestUIHandler|
// - |TestUIHandler| replies with "ack" message
// - page replies back with "fin"
//
// Once "fin" is received |IsFinReceived()| call will return true, indicating
// that communication was successful. See test WebUI page code here:
// ios/web/test/data/mojo_test.js
class TestUIHandler : public TestUIHandlerMojo,
public shell::InterfaceFactory<TestUIHandlerMojo> {
public:
TestUIHandler() {}
~TestUIHandler() override {}
// Returns true if "fin" has been received.
bool IsFinReceived() { return fin_received_; }
// TestUIHandlerMojo overrides.
void HandleJsMessage(const mojo::String& message, TestPagePtr page) override {
if (message.get() == "syn") {
// Received "syn" message from WebUI page, send "ack" as reply.
DCHECK(!syn_received_);
DCHECK(!fin_received_);
syn_received_ = true;
NativeMessageResultMojoPtr result(NativeMessageResultMojo::New());
result->message = mojo::String::From("ack");
page->HandleNativeMessage(std::move(result));
} else if (message.get() == "fin") {
// Received "fin" from the WebUI page in response to "ack".
DCHECK(syn_received_);
DCHECK(!fin_received_);
fin_received_ = true;
} else {
NOTREACHED();
}
}
private:
// shell::InterfaceFactory overrides.
void Create(shell::Connection* connection,
mojo::InterfaceRequest<TestUIHandlerMojo> request) override {
bindings_.AddBinding(this, std::move(request));
}
mojo::BindingSet<TestUIHandlerMojo> bindings_;
// |true| if "syn" has been received.
bool syn_received_ = false;
// |true| if "fin" has been received.
bool fin_received_ = false;
};
// Controller for test WebUI.
class TestUI : public WebUIIOSController {
public:
// Constructs controller from |web_ui| and |ui_handler| which will communicate
// with test WebUI page.
TestUI(WebUIIOS* web_ui, TestUIHandler* ui_handler)
: WebUIIOSController(web_ui) {
web::WebUIIOSDataSource* source =
web::WebUIIOSDataSource::Create(kTestWebUIURLHost);
source->AddResourcePath("mojo_test.js", IDR_MOJO_TEST_JS);
source->AddResourcePath("ios/web/test/mojo_test.mojom",
IDR_MOJO_TEST_MOJO_JS);
source->SetDefaultResource(IDR_MOJO_TEST_HTML);
web::WebState* web_state = web_ui->GetWebState();
web::WebUIIOSDataSource::Add(web_state->GetBrowserState(), source);
web_state->GetMojoInterfaceRegistry()->AddInterface(ui_handler);
}
};
// Factory that creates TestUI controller.
class TestWebUIControllerFactory : public WebUIIOSControllerFactory {
public:
// Constructs a controller factory which will eventually create |ui_handler|.
explicit TestWebUIControllerFactory(TestUIHandler* ui_handler)
: ui_handler_(ui_handler) {}
// WebUIIOSControllerFactory overrides.
WebUIIOSController* CreateWebUIIOSControllerForURL(
WebUIIOS* web_ui,
const GURL& url) const override {
DCHECK_EQ(url.scheme(), kTestWebUIScheme);
DCHECK_EQ(url.host(), kTestWebUIURLHost);
return new TestUI(web_ui, ui_handler_);
}
private:
// UI handler class which communicates with test WebUI page.
TestUIHandler* ui_handler_;
};
} // namespace
// A test fixture for verifying mojo comminication for WebUI.
class WebUIMojoTest : public WebIntTest {
protected:
WebUIMojoTest()
: web_state_(new WebStateImpl(GetBrowserState())),
ui_handler_(new TestUIHandler()) {
web_state_->GetNavigationManagerImpl().InitializeSession(nil, nil, NO, 0);
WebUIIOSControllerFactory::RegisterFactory(
new TestWebUIControllerFactory(ui_handler_.get()));
}
// Returns WebState which loads test WebUI page.
WebStateImpl* web_state() { return web_state_.get(); }
// Returns UI handler which communicates with WebUI page.
TestUIHandler* test_ui_handler() { return ui_handler_.get(); }
private:
std::unique_ptr<WebStateImpl> web_state_;
std::unique_ptr<TestUIHandler> ui_handler_;
};
// Tests that JS can send messages to the native code and vice versa.
// TestUIHandler is used for communication and test suceeds only when
// |TestUIHandler| sucessfully receives "ack" message from WebUI page.
TEST_F(WebUIMojoTest, MessageExchange) {
web_state()->SetWebUsageEnabled(true);
web_state()->GetWebController().useMojoForWebUI = YES;
web_state()->GetView(); // WebState won't load URL without view.
NavigationManager::WebLoadParams load_params(GURL(
url::SchemeHostPort(kTestWebUIScheme, kTestWebUIURLHost, 0).Serialize()));
web_state()->GetNavigationManager()->LoadURLWithParams(load_params);
// Wait until |TestUIHandler| receives "ack" message from WebUI page.
base::test::ios::WaitUntilCondition(^{
base::RunLoop().RunUntilIdle();
return test_ui_handler()->IsFinReceived();
});
}
} // namespace web
...@@ -183,6 +183,11 @@ ...@@ -183,6 +183,11 @@
"ios/web/ios_web_resources.grd": { "ios/web/ios_web_resources.grd": {
"includes": [23950], "includes": [23950],
}, },
# ios/web test strings and content/shell strings must start at the same id.
# App only use one file depending on whether it is iOS or other platform.
"ios/web/test/test_resources.grd": {
"includes": [24450],
},
"content/shell/shell_resources.grd": { "content/shell/shell_resources.grd": {
"includes": [24450], "includes": [24450],
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment