blob: 87144e07c6495359d2292cfe909a1dae7cc631d3 [file] [log] [blame]
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script type="module">
import {
TestMessageTargetReceiver,
TestMessageTarget_NestedEnum,
TestMessageTarget,
TestMessageTargetCallbackRouter,
SubinterfaceCallbackRouter,
SubinterfaceClientCallbackRouter,
SubinterfaceRemote}
from '/gen/content/test/data/mojo_bindings_web_test.test-mojom.m.js';
import {MojoEcho, MojoEchoRemote}
from '/gen/content/web_test/common/mojo_echo.mojom.m.js';
const kTestMessage = 'hello there';
const kTestNumbers = [0, 1, 1, 2, 3, 5, 8, 13, 21];
class TargetImpl {
constructor() {
this.numPokes = 0;
this.target = new TestMessageTargetReceiver(this);
}
poke() { this.numPokes++; }
ping() { return Promise.resolve(); }
repeat(message, numbers) { return {message: message, numbers: numbers}; }
echo(nested) { return Promise.resolve({nested: nested}); }
deconstruct(test_struct) {}
flatten(values) {}
flattenUnions(unions) {}
requestSubinterface(request, client) {}
}
promise_test(() => {
let impl = new TargetImpl;
let remote = impl.target.$.bindNewPipeAndPassRemote();
remote.poke();
return remote.ping().then(() => {
assert_equals(impl.numPokes, 1);
});
}, 'messages with replies return Promises that resolve on reply received');
promise_test(() => {
let impl = new TargetImpl;
let remote = impl.target.$.bindNewPipeAndPassRemote();
return remote.repeat(kTestMessage, kTestNumbers)
.then(reply => {
assert_equals(reply.message, kTestMessage);
assert_array_equals(reply.numbers, kTestNumbers);
});
}, 'implementations can reply with multiple reply arguments');
promise_test(() => {
let impl = new TargetImpl;
let remote = impl.target.$.bindNewPipeAndPassRemote();
const enumValue = TestMessageTarget_NestedEnum.kFoo;
return remote.echo(enumValue)
.then(({nested}) => assert_equals(nested, enumValue));
}, 'nested enums are usable as arguments and responses.');
promise_test(async (t) => {
const impl = new TargetImpl;
const remote = impl.target.$.bindNewPipeAndPassRemote();
await remote.ping();
remote.$.close();
await promise_rejects_js(t, Error, remote.ping());
}, 'after the pipe is closed all future calls should fail');
promise_test(async (t) => {
const impl = new TargetImpl;
const remote = impl.target.$.bindNewPipeAndPassRemote();
// None of these promises should successfully resolve because we are
// immediately closing the pipe.
const promises = []
for (let i = 0; i < 10; i++) {
promises.push(remote.ping());
}
remote.$.close();
for (const promise of promises) {
await promise_rejects_js(t, Error, promise);
}
}, 'closing the pipe drops any pending messages');
promise_test(() => {
let impl = new TargetImpl;
// Intercept any browser-bound request for TestMessageTarget and bind it
// instead to the local |impl| object.
let interceptor = new MojoInterfaceInterceptor(
TestMessageTarget.$interfaceName);
interceptor.oninterfacerequest = e => {
impl.target.$.bindHandle(e.handle);
}
interceptor.start();
let remote = TestMessageTarget.getRemote();
remote.poke();
return remote.ping().then(() => {
assert_equals(impl.numPokes, 1);
});
}, 'getRemote() attempts to send requests to the frame host');
promise_test(() => {
let router = new TestMessageTargetCallbackRouter;
let remote = router.$.bindNewPipeAndPassRemote();
return new Promise(resolve => {
router.poke.addListener(resolve);
remote.poke();
});
}, 'basic generated CallbackRouter behavior works as intended');
promise_test(() => {
let router = new TestMessageTargetCallbackRouter;
let remote = router.$.bindNewPipeAndPassRemote();
let numPokes = 0;
router.poke.addListener(() => ++numPokes);
router.ping.addListener(() => Promise.resolve());
remote.poke();
return remote.ping().then(() => assert_equals(numPokes, 1));
}, 'CallbackRouter listeners can reply to messages');
promise_test(() => {
let router = new TestMessageTargetCallbackRouter;
let remote = router.$.bindNewPipeAndPassRemote();
router.repeat.addListener(
(message, numbers) => ({message: message, numbers: numbers}));
return remote.repeat(kTestMessage, kTestNumbers)
.then(reply => {
assert_equals(reply.message, kTestMessage);
assert_array_equals(reply.numbers, kTestNumbers);
});
}, 'CallbackRouter listeners can reply with multiple reply arguments');
promise_test(() => {
let targetRouter = new TestMessageTargetCallbackRouter;
let targetRemote = targetRouter.$.bindNewPipeAndPassRemote();
let subinterfaceRouter = new SubinterfaceCallbackRouter;
targetRouter.requestSubinterface.addListener((request, client) => {
let values = [];
subinterfaceRouter.$.bindHandle(request.handle);
subinterfaceRouter.push.addListener(value => values.push(value));
subinterfaceRouter.flush.addListener(() => {
client.didFlush(values);
values = [];
});
});
let clientRouter = new SubinterfaceClientCallbackRouter;
let subinterfaceRemote = new SubinterfaceRemote;
targetRemote.requestSubinterface(
subinterfaceRemote.$.bindNewPipeAndPassReceiver(),
clientRouter.$.bindNewPipeAndPassRemote());
return new Promise(resolve => {
clientRouter.didFlush.addListener(values => {
assert_array_equals(values, kTestNumbers);
resolve();
});
kTestNumbers.forEach(n => subinterfaceRemote.push(n));
subinterfaceRemote.flush();
});
}, 'can send and receive interface requests and proxies');
promise_test(() => {
const targetRouter = new TestMessageTargetCallbackRouter;
const targetRemote = targetRouter.$.bindNewPipeAndPassRemote();
targetRouter.deconstruct.addListener(({x, y, z}) => ({
x: x,
y: y,
z: z
}));
return targetRemote.deconstruct({x: 1}).then(reply => {
assert_equals(reply.x, 1);
assert_equals(reply.y, 2);
assert_equals(reply.z, 1);
});
}, 'structs with default values from nested enums and constants are ' +
'correctly serialized');
promise_test(() => {
const targetRouter = new TestMessageTargetCallbackRouter;
const targetRemote = targetRouter.$.bindNewPipeAndPassRemote();
targetRouter.flatten.addListener(values => ({values: values.map(v => v.x)}));
return targetRemote.flatten([{x: 1}, {x: 2}, {x: 3}]).then(reply => {
assert_array_equals(reply.values, [1, 2, 3]);
});
}, 'regression test for complex array serialization');
promise_test(() => {
const targetRouter = new TestMessageTargetCallbackRouter;
const targetRemote = targetRouter.$.bindNewPipeAndPassRemote();
targetRouter.flattenUnions.addListener(unions => {
return {x: unions.filter(u => u.x !== undefined).map(u => u.x),
s: unions.filter(u => u.s !== undefined).map(u => u.s.x)};
});
return targetRemote.flattenUnions(
[{x: 1}, {x: 2}, {s: {x: 3}}, {s: {x: 4}}, {x: 5}, {s: {x: 6}}])
.then(reply => {
assert_array_equals(reply.x, [1, 2, 5]);
assert_array_equals(reply.s, [3, 4, 6]);
});
}, 'can serialize and deserialize unions');
promise_test(() => {
let impl = new TargetImpl;
let remote = impl.target.$.bindNewPipeAndPassRemote();
// Poke a bunch of times. These should never race with the assertion below,
// because the |flushForTesting| request/response is ordered against other
// messages on |remote|.
const kNumPokes = 100;
for (let i = 0; i < kNumPokes; ++i)
remote.poke();
return remote.$.flushForTesting().then(() => {
assert_equals(impl.numPokes, kNumPokes);
});
}, 'can use generated flushForTesting API for synchronization in tests');
promise_test(async(t) => {
const impl = new TargetImpl;
const remote = impl.target.$.bindNewPipeAndPassRemote();
const disconnectPromise = new Promise(resolve => impl.target.onConnectionError.addListener(resolve));
remote.$.close();
return disconnectPromise;
}, 'InterfaceTarget connection error handler runs when set on an Interface object');
promise_test(() => {
const router = new TestMessageTargetCallbackRouter;
const remote = router.$.bindNewPipeAndPassRemote();
const disconnectPromise = new Promise(resolve => router.onConnectionError.addListener(resolve));
remote.$.close();
return disconnectPromise;
}, 'InterfaceTarget connection error handler runs when set on an InterfaceCallbackRouter object');
function getMojoEchoRemote() {
// MojoEchoRemote.getRemote() only works for frame interfaces
// and MojoEcho is a process interface.
let remote = new MojoEchoRemote;
remote.$.bindNewPipeAndPassReceiver().bindInBrowser('process');
return remote;
}
promise_test(async () => {
const remote = getMojoEchoRemote();
{
const {value} = await remote.echoBoolFromUnion({boolValue: true});
assert_true(value);
}
{
const {value} = await remote.echoInt32FromUnion({int32Value: 123});
assert_equals(value, 123);
}
{
const {value} = await remote.echoStringFromUnion({stringValue: "foo"});
assert_equals(value, "foo");
}
}, 'JS encoding and C++ decoding of unions work as expected.');
promise_test(async() => {
const remote = getMojoEchoRemote();
{
const {testUnion: {boolValue}} = await remote.echoBoolAsUnion(true);
assert_equals(boolValue, true);
}
{
const {testUnion: {int32Value}} = await remote.echoInt32AsUnion(123);
assert_equals(int32Value, 123);
}
{
const {testUnion: {stringValue}} = await remote.echoStringAsUnion("foo");
assert_equals(stringValue, "foo");
}
}, 'JS decoding and C++ encoding of unions work as expected.');
promise_test(async () => {
const remote = getMojoEchoRemote();
{
const response = await remote.echoNullFromOptionalUnion();
assert_equals(Object.keys(response).length, 0);
}
{
const {value} = await remote.echoBoolFromOptionalUnion({boolValue: true});
assert_true(value);
}
{
const {value} = await remote.echoInt32FromOptionalUnion({int32Value: 123});
assert_equals(value, 123);
}
{
const {value} = await remote.echoStringFromOptionalUnion({stringValue: "foo"});
assert_equals(value, "foo");
}
}, 'JS encoding and C++ decoding of optional unions work as expected.');
promise_test(async() => {
const remote = getMojoEchoRemote();
{
const {testUnion} = await remote.echoNullAsOptionalUnion();
assert_equals(testUnion, null);
}
{
const {testUnion: {boolValue}} = await remote.echoBoolAsOptionalUnion(true);
assert_equals(boolValue, true);
}
{
const {testUnion: {int32Value}} = await remote.echoInt32AsOptionalUnion(123);
assert_equals(int32Value, 123);
}
{
const {testUnion: {stringValue}} =
await remote.echoStringAsOptionalUnion("foo");
assert_equals(stringValue, "foo");
}
}, 'JS decoding and C++ encoding of optional unions work as expected.');
promise_test(async() => {
const remote = getMojoEchoRemote();
{
const {value} = await remote.echoInt8FromNestedUnion({int8Value: -10});
assert_equals(value, -10);
}
{
const {value} = await remote.echoBoolFromNestedUnion({unionValue: {boolValue: true}});
assert_true(value);
}
{
const {value} = await remote.echoStringFromNestedUnion({unionValue: {stringValue: 'foo'}});
assert_equals(value, 'foo');
}
}, 'JS encoding and C++ decoding of nested unions work as expected.');
promise_test(async() => {
const remote = getMojoEchoRemote();
{
const {testUnion: {int8Value}} = await remote.echoInt8AsNestedUnion(-10);
assert_equals(int8Value, -10);
}
{
const {testUnion: {unionValue: {boolValue}}} = await remote.echoBoolAsNestedUnion(true);
assert_true(boolValue);
}
{
const {testUnion: {unionValue: {stringValue}}} =
await remote.echoStringAsNestedUnion('foo');
assert_equals(stringValue, 'foo');
}
}, 'JS decoding and C++ encoding of nested unions work as expected.');
promise_test(async() => {
const remote = getMojoEchoRemote();
{
const response = await remote.echoNullFromOptionalNestedUnion();
assert_equals(Object.keys(response).length, 0);
}
{
const {value} = await remote.echoInt8FromOptionalNestedUnion({int8Value: -10});
assert_equals(value, -10);
}
{
const {value} = await remote.echoBoolFromOptionalNestedUnion({unionValue: {boolValue: true}});
assert_true(value);
}
{
const {value} = await remote.echoStringFromOptionalNestedUnion({unionValue: {stringValue: 'foo'}});
assert_equals(value, 'foo');
}
}, 'JS encoding and C++ decoding of optional nested unions work as expected.');
promise_test(async() => {
const remote = getMojoEchoRemote();
{
const {testUnion} = await remote.echoNullAsOptionalNestedUnion();
assert_equals(testUnion, null);
}
{
const {testUnion: {int8Value}} = await remote.echoInt8AsOptionalNestedUnion(-10);
assert_equals(int8Value, -10);
}
{
const {testUnion: {unionValue: {boolValue}}} =
await remote.echoBoolAsOptionalNestedUnion(true);
assert_true(boolValue);
}
{
const {testUnion: {unionValue: {stringValue}}} =
await remote.echoStringAsOptionalNestedUnion('foo');
assert_equals(stringValue, 'foo');
}
}, 'JS decoding and C++ encoding of optional nested unions work as expected.');
</script>