txedge-js
Version:
Edge.js: run .NET and Node.js in-process on Windows, Mac OS, and Linux
293 lines (265 loc) • 8.41 kB
JavaScript
/**
* Portions Copyright (c) Microsoft Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
* OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
* ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR
* PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
*
* See the Apache Version 2.0 License for specific language governing
* permissions and limitations under the License.
*/
var edge = require('../lib/edge.js'), assert = require('assert')
, path = require('path');
var edgeTestDll = process.env.EDGE_USE_CORECLR ? 'test' : path.join(__dirname, 'Edge.Tests.dll');
var prefix = process.env.EDGE_USE_CORECLR ? '[CoreCLR]' : process.platform === 'win32' ? '[.NET]' : '[Mono]';
describe('async call from .net to node.js', function () {
it(prefix + ' succeeds for hello world', function (done) {
var func = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'InvokeBack'
});
var payload = {
hello: function (payload, callback) {
callback(null, 'Node.js welcomes ' + payload);
}
};
func(payload, function (error, result) {
assert.ifError(error);
assert.equal(result, 'Node.js welcomes .NET');
done();
});
});
it(prefix + ' successfuly marshals data from .net to node.js', function (done) {
var func = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'MarshalInFromNet'
});
var payload = {
hello: function (result, callback) {
assert.equal(typeof result, 'object');
assert.ok(result.a === 1);
assert.ok(result.b === 3.1415);
assert.ok(result.c === 'foo');
assert.ok(result.d === true);
assert.ok(result.e === false);
assert.equal(typeof result.f, 'object');
assert.ok(Buffer.isBuffer(result.f));
assert.equal(result.f.length, 10);
assert.ok(Array.isArray(result.g));
assert.equal(result.g.length, 2);
assert.ok(result.g[0] === 1);
assert.ok(result.g[1] === 'foo');
assert.equal(typeof result.h, 'object');
assert.ok(result.h.a === 'foo');
assert.ok(result.h.b === 12);
assert.equal(typeof result.i, 'function');
assert.equal(typeof result.j, 'object');
assert.ok(result.j.valueOf() === Date.UTC(2013, 07, 30));
callback(null, 'yes');
}
};
func(payload, function (error, result) {
assert.ifError(error);
assert.equal(result, 'yes');
done();
});
});
it(prefix + ' successfuly marshals object hierarchy from .net to node.js', function (done) {
var func = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'MarshalObjectHierarchy'
});
func(null, function (error, result) {
assert.ifError(error);
assert.equal(result.A_field, 'a_field');
assert.equal(result.A_prop, 'a_prop');
assert.equal(result.B_field, 'b_field');
assert.equal(result.B_prop, 'b_prop');
done();
});
});
it(prefix + ' successfuly marshals data from node.js to .net', function (done) {
var func = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'MarshalBackToNet'
});
var payload = {
hello: function (result, callback) {
var payload = {
a: 1,
b: 3.1415,
c: 'foo',
d: true,
e: false,
f: new Buffer(10),
g: [ 1, 'foo' ],
h: { a: 'foo', b: 12 },
j: new Date(Date.UTC(2013, 07, 30))
};
callback(null, payload);
}
};
func(payload, function (error, result) {
assert.ifError(error);
assert.equal(result, 'yes');
done();
});
});
it(prefix + ' successfuly handles process.nextTick() in JS callback', function (done) {
var func = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'InvokeBack'
});
var payload = {
hello: function (payload, callback) {
process.nextTick(function() {
callback(null, 'Node.js welcomes ' + payload);
});
}
};
func(payload, function (error, result) {
assert.ifError(error);
assert.equal(result, 'Node.js welcomes .NET');
done();
});
});
it(prefix + ' successfuly marshals v8 exception on invoking thread', function (done) {
var func = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'MarshalException'
});
var payload = {
hello: function (result, callback) {
throw new Error('Sample Node.js exception');
}
};
func(payload, function (error, result) {
assert.ifError(error);
assert.equal(typeof result, 'string');
assert.ok(result.indexOf('Sample Node.js exception') > 0);
done();
});
});
it(prefix + ' successfuly marshals v8 exception in callback', function (done) {
var func = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'MarshalException'
});
var payload = {
hello: function (result, callback) {
var next = global.setImmediate || process.nextTick;
next(function () {
callback(new Error('Sample Node.js exception'));
});
}
};
func(payload, function (error, result) {
assert.ifError(error);
assert.equal(typeof result, 'string');
assert.ok(result.indexOf('Sample Node.js exception') > 0);
done();
});
});
it(prefix + ' successfuly marshals empty buffer', function (done) {
var func = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'ReturnEmptyBuffer'
});
func(null, function (error, result) {
assert.ifError(error);
assert.ok(Buffer.isBuffer(result));
assert.ok(result.length === 0)
done();
})
});
});
describe('delayed call from node.js to .net', function () {
it(prefix + ' succeeds for one callback after Task', function (done) {
var expected = [
'InvokeBackAfterCLRCallHasFinished#EnteredCLR',
'InvokeBackAfterCLRCallHasFinished#LeftCLR',
'InvokeBackAfterCLRCallHasFinished#ReturnedToNode',
'InvokeBackAfterCLRCallHasFinished#CallingCallbackFromDelayedTask',
];
var func = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'InvokeBackAfterCLRCallHasFinished'
});
var ensureNodejsFuncIsCollected = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'EnsureNodejsFuncIsCollected'
});
var trace = [];
var payload = {
eventCallback: function(result, callback) {
trace.push(result);
callback();
assert.ok(expected.length === trace.length);
for (var i = 0; i < expected.length; i++) {
assert.ok(expected[i] === trace[i]);
}
// Check for collections after the callback is completed
// The func is still referenced by the callback context so it won't be collected if we run inline
setTimeout(function() {
ensureNodejsFuncIsCollected(null, function(error, result) {
assert.ifError(error);
assert.ok(result);
done();
});
}, 10);
}
};
func(payload, function (error, result) {
assert.ifError(error);
result.forEach(function(entry) {
trace.push(entry);
});
trace.push('InvokeBackAfterCLRCallHasFinished#ReturnedToNode');
});
});
});
describe('.net returns Func to node.js', function () {
it(prefix + ' releases the func', function (done) {
assert.ok(global.gc, 'This test must be run with --expose-gc set');
var returnDotNetFunc = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'ReturnDotNetFunc'
});
var ensureDotNetFuncIsCollected = edge.func({
assemblyFile: edgeTestDll,
typeName: 'Edge.Tests.Startup',
methodName: 'EnsureDotNetFuncIsCollected'
});
returnDotNetFunc(null, function(error, result) {
assert.ifError(error);
// Check for collections after the callback is completed
// The func is still referenced by the callback context so it won't be collected if we run inline
setTimeout(() => {
// Force a GC to release the func returned by returnDotNetFunc is freed
global.gc();
ensureDotNetFuncIsCollected(null, function(error, result) {
assert.ifError(error);
assert.ok(result);
done();
});
});
});
});
});