blob: 0b0a71b2d03a217173fc251b0f5ee776b22be037 [file] [log] [blame]
// Copyright 2013 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.
// Mock out the support module to avoid depending on the message loop.
define("mojo/public/js/bindings/support", ["timer"], function(timer) {
var waitingCallbacks = [];
function WaitCookie(id) {
this.id = id;
}
function asyncWait(handle, flags, callback) {
var id = waitingCallbacks.length;
waitingCallbacks.push(callback);
return new WaitCookie(id);
}
function cancelWait(cookie) {
waitingCallbacks[cookie.id] = null;
}
function numberOfWaitingCallbacks() {
var count = 0;
for (var i = 0; i < waitingCallbacks.length; ++i) {
if (waitingCallbacks[i])
++count;
}
return count;
}
function pumpOnce(result) {
var callbacks = waitingCallbacks;
waitingCallbacks = [];
for (var i = 0; i < callbacks.length; ++i) {
if (callbacks[i])
callbacks[i](result);
}
}
// Queue up a pumpOnce call to execute after the stack unwinds. Use
// this to trigger a pump after all Promises are executed.
function queuePump(result) {
timer.createOneShot(0, pumpOnce.bind(undefined, result));
}
var exports = {};
exports.asyncWait = asyncWait;
exports.cancelWait = cancelWait;
exports.numberOfWaitingCallbacks = numberOfWaitingCallbacks;
exports.pumpOnce = pumpOnce;
exports.queuePump = queuePump;
return exports;
});
define([
"gin/test/expect",
"mojo/public/js/bindings/support",
"mojo/public/js/bindings/core",
"mojo/public/js/bindings/connection",
"mojo/public/interfaces/bindings/tests/sample_interfaces.mojom",
"mojo/public/interfaces/bindings/tests/sample_service.mojom",
"mojo/apps/js/bindings/threading",
"gc",
], function(expect,
mockSupport,
core,
connection,
sample_interfaces,
sample_service,
threading,
gc) {
testClientServer();
testWriteToClosedPipe();
testRequestResponse().then(function() {
this.result = "PASS";
gc.collectGarbage(); // should not crash
threading.quit();
}.bind(this)).catch(function(e) {
this.result = "FAIL: " + (e.stack || e);
threading.quit();
}.bind(this));
function testClientServer() {
var receivedFrobinate = false;
var receivedDidFrobinate = false;
// ServiceImpl -------------------------------------------------------------
function ServiceImpl(peer) {
this.peer = peer;
}
ServiceImpl.prototype = Object.create(sample_service.ServiceStub.prototype);
ServiceImpl.prototype.frobinate = function(foo, baz, port) {
receivedFrobinate = true;
expect(foo.name).toBe("Example name");
expect(baz).toBeTruthy();
expect(core.close(port)).toBe(core.RESULT_OK);
this.peer.didFrobinate(42);
};
// ServiceImpl -------------------------------------------------------------
function ServiceClientImpl(peer) {
this.peer = peer;
}
ServiceClientImpl.prototype =
Object.create(sample_service.ServiceClientStub.prototype);
ServiceClientImpl.prototype.didFrobinate = function(result) {
receivedDidFrobinate = true;
expect(result).toBe(42);
};
var pipe = core.createMessagePipe();
var anotherPipe = core.createMessagePipe();
var sourcePipe = core.createMessagePipe();
var connection0 = new connection.Connection(
pipe.handle0, ServiceImpl, sample_service.ServiceClientProxy);
var connection1 = new connection.Connection(
pipe.handle1, ServiceClientImpl, sample_service.ServiceProxy);
var foo = new sample_service.Foo();
foo.bar = new sample_service.Bar();
foo.name = "Example name";
foo.source = sourcePipe.handle0;
connection1.remote.frobinate(foo, true, anotherPipe.handle0);
mockSupport.pumpOnce(core.RESULT_OK);
expect(receivedFrobinate).toBeTruthy();
expect(receivedDidFrobinate).toBeTruthy();
connection0.close();
connection1.close();
expect(mockSupport.numberOfWaitingCallbacks()).toBe(0);
// sourcePipe.handle0 was closed automatically when sent over IPC.
expect(core.close(sourcePipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
// sourcePipe.handle1 hasn't been closed yet.
expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK);
// anotherPipe.handle0 was closed automatically when sent over IPC.
expect(core.close(anotherPipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
// anotherPipe.handle1 hasn't been closed yet.
expect(core.close(anotherPipe.handle1)).toBe(core.RESULT_OK);
// The Connection object is responsible for closing these handles.
expect(core.close(pipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
expect(core.close(pipe.handle1)).toBe(core.RESULT_INVALID_ARGUMENT);
}
function testWriteToClosedPipe() {
var pipe = core.createMessagePipe();
var connection1 = new connection.Connection(
pipe.handle1, function() {}, sample_service.ServiceProxy);
// Close the other end of the pipe.
core.close(pipe.handle0);
// Not observed yet because we haven't pumped events yet.
expect(connection1.encounteredError()).toBeFalsy();
var foo = new sample_service.Foo();
foo.bar = new sample_service.Bar();
// TODO(darin): crbug.com/357043: pass null in place of |foo| here.
connection1.remote.frobinate(foo, true, null);
// Write failures are not reported.
expect(connection1.encounteredError()).toBeFalsy();
// Pump events, and then we should start observing the closed pipe.
mockSupport.pumpOnce(core.RESULT_OK);
expect(connection1.encounteredError()).toBeTruthy();
connection1.close();
}
function testRequestResponse() {
// ProviderImpl ------------------------------------------------------------
function ProviderImpl(peer) {
this.peer = peer;
}
ProviderImpl.prototype =
Object.create(sample_interfaces.ProviderStub.prototype);
ProviderImpl.prototype.echoString = function(a) {
mockSupport.queuePump(core.RESULT_OK);
return Promise.resolve({a: a});
};
ProviderImpl.prototype.echoStrings = function(a, b) {
mockSupport.queuePump(core.RESULT_OK);
return Promise.resolve({a: a, b: b});
};
// ProviderClientImpl ------------------------------------------------------
function ProviderClientImpl(peer) {
this.peer = peer;
}
ProviderClientImpl.prototype =
Object.create(sample_interfaces.ProviderClientStub.prototype);
var pipe = core.createMessagePipe();
var connection0 = new connection.Connection(
pipe.handle0, ProviderImpl, sample_interfaces.ProviderClientProxy);
var connection1 = new connection.Connection(
pipe.handle1, ProviderClientImpl, sample_interfaces.ProviderProxy);
var origReadMessage = core.readMessage;
// echoString
mockSupport.queuePump(core.RESULT_OK);
return connection1.remote.echoString("hello").then(function(response) {
expect(response.a).toBe("hello");
}).then(function() {
// echoStrings
mockSupport.queuePump(core.RESULT_OK);
return connection1.remote.echoStrings("hello", "world");
}).then(function(response) {
expect(response.a).toBe("hello");
expect(response.b).toBe("world");
}).then(function() {
// Mock a read failure, expect it to fail.
core.readMessage = function() {
return { result: core.RESULT_UNKNOWN };
};
mockSupport.queuePump(core.RESULT_OK);
return connection1.remote.echoString("goodbye");
}).then(function() {
throw Error("Expected echoString to fail.");
}, function(error) {
expect(error.message).toBe("Connection error: " + core.RESULT_UNKNOWN);
// Clean up.
core.readMessage = origReadMessage;
});
}
});