You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
5.2 KiB
206 lines
5.2 KiB
'use strict'; |
|
|
|
var crypto = require('crypto'); |
|
var Connection = require('../lib/connection').Connection; |
|
var PassThrough = |
|
require('stream').PassThrough || |
|
require('readable-stream/passthrough'); |
|
var defer = require('when').defer; |
|
var defs = require('../lib/defs'); |
|
var assert = require('assert'); |
|
|
|
var schedule = (typeof setImmediate === 'function') ? |
|
setImmediate : process.nextTick; |
|
|
|
function randomString() { |
|
var hash = crypto.createHash('sha1'); |
|
hash.update(crypto.randomBytes(64)); |
|
return hash.digest('base64'); |
|
} |
|
|
|
|
|
// Set up a socket pair {client, server}, such that writes to the |
|
// client are readable from the server, and writes to the server are |
|
// readable at the client. |
|
// |
|
// +---+ +---+ |
|
// | C | | S | |
|
// --write->| l |----->| e |--read--> |
|
// | i | | r | |
|
// <--read--| e |<-----| v |<-write-- |
|
// | n | | e | |
|
// | t | | r | |
|
// +---+ +---+ |
|
// |
|
// I also need to make sure that end called on either socket affects |
|
// the other. |
|
|
|
function socketPair() { |
|
var server = new PassThrough(); |
|
var client = new PassThrough(); |
|
server.write = client.push.bind(client); |
|
client.write = server.push.bind(server); |
|
function end(chunk, encoding) { |
|
if (chunk) this.push(chunk, encoding); |
|
this.push(null); |
|
} |
|
server.end = end.bind(client); |
|
client.end = end.bind(server); |
|
|
|
return {client: client, server: server}; |
|
} |
|
|
|
function runServer(socket, run) { |
|
var frames = new Connection(socket); |
|
// We will be closing the socket without doing a closing handshake, |
|
// so cheat |
|
frames.expectSocketClose = true; |
|
// We also need to create some channel buffers, again a cheat |
|
frames.freshChannel(null); |
|
frames.freshChannel(null); |
|
frames.freshChannel(null); |
|
|
|
function send(id, fields, channel, content) { |
|
channel = channel || 0; |
|
if (content) { |
|
schedule(function() { |
|
frames.sendMessage(channel, id, fields, |
|
defs.BasicProperties, fields, |
|
content); |
|
}); |
|
} |
|
else { |
|
schedule(function() { |
|
frames.sendMethod(channel, id, fields); |
|
}); |
|
} |
|
} |
|
|
|
function await(method) { |
|
return function() { |
|
var d = defer(); |
|
if (method) { |
|
frames.step(function(e, f) { |
|
if (e !== null) return d.reject(e); |
|
if (f.id === method) |
|
d.resolve(f); |
|
else |
|
d.reject(new Error("Expected method: " + method + |
|
", got " + f.id)); |
|
}); |
|
} |
|
else { |
|
frames.step(function(e, f) { |
|
if (e !== null) return d.reject(e); |
|
else d.resolve(f); |
|
}); |
|
} |
|
return d.promise; |
|
}; |
|
} |
|
run(send, await); |
|
return frames; |
|
} |
|
|
|
// Produce a callback that will complete the test successfully |
|
function succeed(done) { |
|
return function() { done(); } |
|
} |
|
|
|
// Produce a callback that will fail the test, given either an error |
|
// (to be used as a failure continuation) or any other value (to be |
|
// used as a success continuation when failure is expected) |
|
function fail(done) { |
|
return function(err) { |
|
if (err instanceof Error) done(err); |
|
else done(new Error("Expected to fail, instead got " + err.toString())); |
|
} |
|
} |
|
|
|
// Create a function that will call done once it's been called itself |
|
// `count` times. If it's called with an error value, it will |
|
// immediately call done with that error value. |
|
function latch(count, done) { |
|
var awaiting = count; |
|
var alive = true; |
|
return function(err) { |
|
if (err instanceof Error && alive) { |
|
alive = false; |
|
done(err); |
|
} |
|
else { |
|
awaiting--; |
|
if (awaiting === 0 && alive) { |
|
alive = false; |
|
done(); |
|
} |
|
} |
|
}; |
|
} |
|
|
|
// Call a thunk with a continuation that will be called with an error |
|
// if the thunk throws one, or nothing if it runs to completion. |
|
function completes(thunk, done) { |
|
try { |
|
thunk(); |
|
done(); |
|
} |
|
catch (e) { done(e); } |
|
} |
|
|
|
// Construct a Node.JS-style callback from a success continuation and |
|
// an error continuation |
|
function kCallback(k, ek) { |
|
return function(err, val) { |
|
if (err === null) k && k(val); |
|
else ek && ek(err); |
|
}; |
|
} |
|
|
|
// A noddy way to make tests depend on the node version. |
|
function versionGreaterThan(actual, spec) { |
|
|
|
function int(e) { return parseInt(e); } |
|
|
|
var version = actual.split('.').map(int); |
|
var desired = spec.split('.').map(int); |
|
for (var i=0; i < desired.length; i++) { |
|
var a = version[i], b = desired[i]; |
|
if (a != b) return a > b; |
|
} |
|
return false; |
|
} |
|
|
|
suite('versionGreaterThan', function() { |
|
|
|
test('full spec', function() { |
|
assert(versionGreaterThan('0.8.26', '0.6.12')); |
|
assert(versionGreaterThan('0.8.26', '0.8.21')); |
|
}); |
|
|
|
test('partial spec', function() { |
|
assert(versionGreaterThan('0.9.12', '0.8')); |
|
}); |
|
|
|
test('not greater', function() { |
|
assert(!versionGreaterThan('0.8.12', '0.8.26')); |
|
assert(!versionGreaterThan('0.6.2', '0.6.12')); |
|
assert(!versionGreaterThan('0.8.29', '0.8')); |
|
}); |
|
|
|
test |
|
|
|
}); |
|
|
|
module.exports = { |
|
socketPair: socketPair, |
|
runServer: runServer, |
|
succeed: succeed, |
|
fail: fail, |
|
latch: latch, |
|
completes: completes, |
|
kCallback: kCallback, |
|
schedule: schedule, |
|
randomString: randomString, |
|
versionGreaterThan: versionGreaterThan |
|
};
|
|
|