371 lines
7.0 KiB
JavaScript
371 lines
7.0 KiB
JavaScript
|
|
||
|
var test = require('tape').test
|
||
|
, stream = require('readable-stream')
|
||
|
, duplexer = require('./')
|
||
|
, http = require('http')
|
||
|
, util = require('util')
|
||
|
|
||
|
function MyWritable(t) {
|
||
|
stream.Writable.call(this)
|
||
|
this._t = t
|
||
|
}
|
||
|
|
||
|
util.inherits(MyWritable, stream.Writable)
|
||
|
|
||
|
MyWritable.prototype._write = function(chunk, enc, cb) {
|
||
|
this._t.equal(chunk.toString(), 'writable')
|
||
|
cb()
|
||
|
}
|
||
|
|
||
|
function MyReadable() {
|
||
|
stream.Readable.call(this)
|
||
|
}
|
||
|
|
||
|
util.inherits(MyReadable, stream.Readable)
|
||
|
|
||
|
MyReadable.prototype._read = function(n) {
|
||
|
this.push('readable')
|
||
|
this.push(null)
|
||
|
}
|
||
|
|
||
|
test('basically works', function(t) {
|
||
|
t.plan(2)
|
||
|
|
||
|
var writable = new MyWritable(t)
|
||
|
, readable = new MyReadable()
|
||
|
, instance
|
||
|
|
||
|
instance = duplexer(writable, readable)
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), 'readable')
|
||
|
})
|
||
|
|
||
|
instance.end('writable')
|
||
|
})
|
||
|
|
||
|
test('end event', function(t) {
|
||
|
t.plan(2)
|
||
|
|
||
|
var writable = new stream.PassThrough()
|
||
|
, instance
|
||
|
|
||
|
instance = duplexer(writable, writable)
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), 'a message')
|
||
|
})
|
||
|
|
||
|
instance.on('end', function() {
|
||
|
t.ok(true, 'end happened')
|
||
|
})
|
||
|
|
||
|
instance.end('a message')
|
||
|
})
|
||
|
|
||
|
test('finish event after end', function(t) {
|
||
|
t.plan(2)
|
||
|
|
||
|
var writable = new stream.PassThrough()
|
||
|
, instance
|
||
|
|
||
|
instance = duplexer(writable, writable)
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), 'a message')
|
||
|
})
|
||
|
|
||
|
instance.on('finish', function() {
|
||
|
t.ok(true, 'finish happened')
|
||
|
})
|
||
|
|
||
|
instance.end('a message')
|
||
|
})
|
||
|
|
||
|
test('objectMode', function(t) {
|
||
|
t.plan(1)
|
||
|
|
||
|
var writable = new stream.PassThrough({ objectMode: true })
|
||
|
, instance
|
||
|
|
||
|
instance = duplexer(writable, writable, { objectMode: true })
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.deepEqual(chunk, { hello: 'world' })
|
||
|
})
|
||
|
|
||
|
instance.end({ hello: 'world' })
|
||
|
})
|
||
|
|
||
|
test('pass through error events', function(t) {
|
||
|
// two because it must listen for both readable and
|
||
|
// writable
|
||
|
t.plan(2)
|
||
|
|
||
|
var writable = new stream.PassThrough()
|
||
|
, instance
|
||
|
|
||
|
instance = duplexer(writable, writable)
|
||
|
|
||
|
instance.on('error', function(err) {
|
||
|
t.ok(err, 'an error is emitted')
|
||
|
})
|
||
|
|
||
|
writable.emit('error', new Error('fake!'))
|
||
|
})
|
||
|
|
||
|
test('late hook', function(t) {
|
||
|
t.plan(2)
|
||
|
|
||
|
var writable = new stream.PassThrough()
|
||
|
|
||
|
// nothing here, let's hook them up later
|
||
|
, instance = duplexer()
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), 'writable')
|
||
|
})
|
||
|
|
||
|
instance.on('end', function() {
|
||
|
t.ok(true, 'end happened')
|
||
|
})
|
||
|
|
||
|
instance.end('writable')
|
||
|
|
||
|
// separate hooks for writable
|
||
|
instance.hookWritable(writable)
|
||
|
// and readable
|
||
|
instance.hookReadable(writable)
|
||
|
})
|
||
|
|
||
|
test('late hook reversed', function(t) {
|
||
|
t.plan(1)
|
||
|
|
||
|
var writable = new stream.PassThrough()
|
||
|
|
||
|
// nothing here, let's hook them up later
|
||
|
, instance = duplexer()
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), 'writable')
|
||
|
})
|
||
|
|
||
|
instance.end('writable')
|
||
|
|
||
|
// separate hooks for redable
|
||
|
instance.hookReadable(writable)
|
||
|
// and writable
|
||
|
instance.hookWritable(writable)
|
||
|
})
|
||
|
|
||
|
test('late hook multiple writes', function(t) {
|
||
|
t.plan(4)
|
||
|
|
||
|
var writable = new stream.PassThrough()
|
||
|
|
||
|
// nothing here, let's hook them up later
|
||
|
, instance = duplexer()
|
||
|
, expected = ['a', 'b', 'c']
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), expected.shift())
|
||
|
})
|
||
|
|
||
|
instance.on('end', function() {
|
||
|
t.ok(true, 'end happened')
|
||
|
})
|
||
|
|
||
|
instance.write('a')
|
||
|
instance.write('b')
|
||
|
instance.end('c')
|
||
|
|
||
|
// separate hooks for writable
|
||
|
instance.hookWritable(writable)
|
||
|
// and readable
|
||
|
instance.hookReadable(writable)
|
||
|
})
|
||
|
|
||
|
test('shortcut hook', function(t) {
|
||
|
t.plan(1)
|
||
|
|
||
|
var writable = new stream.PassThrough()
|
||
|
|
||
|
// nothing here, let's hook them up later
|
||
|
, instance = duplexer()
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), 'writable')
|
||
|
})
|
||
|
|
||
|
instance.end('writable')
|
||
|
|
||
|
// single hook for both!
|
||
|
instance.hook(writable, writable)
|
||
|
})
|
||
|
|
||
|
test('double hook', function(t) {
|
||
|
t.plan(2)
|
||
|
|
||
|
var writable = new MyWritable(t)
|
||
|
, readable = new stream.Readable()
|
||
|
|
||
|
// nothing here, let's hook them up later
|
||
|
, instance = duplexer()
|
||
|
|
||
|
writable._write = function(chunk, enc, cb) {
|
||
|
t.equal(chunk.toString(), 'writable')
|
||
|
cb()
|
||
|
}
|
||
|
|
||
|
readable._read = function(n) {
|
||
|
}
|
||
|
|
||
|
instance.hook(writable, readable)
|
||
|
|
||
|
t.test('writable', function(t) {
|
||
|
var thrown = false
|
||
|
|
||
|
try {
|
||
|
instance.hookWritable(writable)
|
||
|
} catch(err) {
|
||
|
thrown = true
|
||
|
}
|
||
|
|
||
|
t.assert(thrown, 'must have thrown')
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t.test('readable', function(t) {
|
||
|
var thrown = false
|
||
|
|
||
|
try {
|
||
|
instance.hookReadable(readable)
|
||
|
} catch(err) {
|
||
|
thrown = true
|
||
|
}
|
||
|
|
||
|
t.assert(thrown, 'must have thrown')
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
test('supports highWaterMark: 2', function(t) {
|
||
|
|
||
|
var writable = new stream.Transform({ objectMode: true, highWaterMark: 2 })
|
||
|
, instance
|
||
|
, unlock = null
|
||
|
|
||
|
writable._transform = function(obj, enc, cb) {
|
||
|
unlock = cb
|
||
|
}
|
||
|
|
||
|
instance = duplexer(writable, writable, { objectMode: true, highWaterMark: 2 })
|
||
|
|
||
|
t.ok(writable.write('hello'), 'after first we can write')
|
||
|
t.notOk(writable.write('hello'), 'after second we cannot write more')
|
||
|
|
||
|
t.end()
|
||
|
})
|
||
|
|
||
|
if (process.browser) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// everything after this line will not be executed
|
||
|
// in the browser
|
||
|
|
||
|
test('HTTP support', function(t) {
|
||
|
t.plan(1)
|
||
|
|
||
|
var server = http.createServer()
|
||
|
, instance = duplexer()
|
||
|
|
||
|
function handle(req, res) {
|
||
|
req.pipe(res)
|
||
|
}
|
||
|
|
||
|
server.on('request', handle)
|
||
|
|
||
|
server.on('listening', function() {
|
||
|
|
||
|
var request = http.request({
|
||
|
host: 'localhost'
|
||
|
, port: server.address().port
|
||
|
, method: 'POST'
|
||
|
, path: '/'
|
||
|
})
|
||
|
|
||
|
request.on('response', function(res) {
|
||
|
instance.hookReadable(res)
|
||
|
})
|
||
|
|
||
|
instance.hookWritable(request)
|
||
|
|
||
|
instance.end('a message')
|
||
|
})
|
||
|
|
||
|
server.listen(0)
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), 'a message')
|
||
|
server.close()
|
||
|
})
|
||
|
})
|
||
|
|
||
|
test('HTTP support with delayed open', function(t) {
|
||
|
t.plan(1)
|
||
|
|
||
|
var server = http.createServer()
|
||
|
, instance = duplexer()
|
||
|
|
||
|
function handle(req, res) {
|
||
|
req.pipe(res)
|
||
|
}
|
||
|
|
||
|
server.on('request', handle)
|
||
|
|
||
|
server.on('listening', function() {
|
||
|
|
||
|
var request = http.request({
|
||
|
host: 'localhost'
|
||
|
, port: server.address().port
|
||
|
, method: 'POST'
|
||
|
, path: '/'
|
||
|
})
|
||
|
|
||
|
request.on('response', function(res) {
|
||
|
instance.hookReadable(res)
|
||
|
})
|
||
|
|
||
|
instance.hookWritable(request)
|
||
|
})
|
||
|
|
||
|
server.listen(0)
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), 'a message')
|
||
|
server.close()
|
||
|
})
|
||
|
|
||
|
instance.end('a message')
|
||
|
})
|
||
|
|
||
|
test('works with a child process', function(t) {
|
||
|
t.plan(1)
|
||
|
|
||
|
var echo = require('child_process').spawn('cat', [], {
|
||
|
stdio: ['pipe', 'pipe', process.stderr]
|
||
|
})
|
||
|
, instance = duplexer(echo.stdin, echo.stdout)
|
||
|
|
||
|
instance.on('data', function(chunk) {
|
||
|
t.equal(chunk.toString(), 'a message')
|
||
|
})
|
||
|
|
||
|
instance.end('a message')
|
||
|
})
|