UNPKG

pipette

Version:

Stream and pipe utilities for Node

444 lines (358 loc) 10 kB
// Copyright 2012 The Obvious Corporation. /* * Modules used */ "use strict"; var assert = require("assert"); var events = require("events"); var pipette = require("../"); var Cat = pipette.Cat; var EventCollector = require("./eventcoll").EventCollector; /* * Helper functions */ /** * Construct an event emitter that emits a single `error` event when resumed. */ function makeErrorBlip(error) { var emitter = new events.EventEmitter(); var valve = new pipette.Valve(emitter, { paused: true }); emitter.emit("error", error); emitter.emit("close"); return valve; } /* * Tests */ /** * Make sure the constructor doesn't fail off the bat. */ function constructor() { new Cat([]); new Cat([new events.EventEmitter()]); new Cat([], {}); new Cat([], { paused: false }); new Cat([], { paused: true }); new Cat([new events.EventEmitter()], { encoding: "utf8" }); new Cat([new events.EventEmitter()], { incomingEncoding: "ucs2" }); } /** * Test expected constructor failures. */ function constructorFailure() { var good = new pipette.Blip("good"); function f1() { new Cat(); } assert.throws(f1, /Invalid streams array/); function f2() { new Cat(["bogus"]); } assert.throws(f2, /Source not an EventEmitter: index 0/); function f3() { new Cat([good, new Buffer(1)]); } assert.throws(f3, /Source not an EventEmitter: index 1/); function f4() { new Cat([good, good, undefined]); } assert.throws(f4, /Missing source: index 2/); function f5() { new Cat([good, good, good, null]); } assert.throws(f5, /Missing source: index 3/); // This is an already-ended Stream-per-se. var bad1 = new pipette.Blip(); bad1.resume(); function f6() { new Cat([good, good, bad1]); } assert.throws(f6, /Source already ended: index 2/); // This is an "apparently ended" readable-stream-like EventEmitter. var bad2 = new events.EventEmitter(); bad2.readable = false; function f7() { new Cat([good, bad2]); } assert.throws(f7, /Source already ended: index 1/); function f8() { new Cat([], { encoding: 12 }); } assert.throws(f8, /Bad value for option: encoding/); function f9() { new Cat([], { incomingEncoding: "zorch" }); } assert.throws(f9, /Bad value for option: incomingEncoding/); function f10() { new Cat([], { paused: undefined }); } assert.throws(f10, /Bad value for option: paused/); function f11() { new Cat([], { zorchSplat: undefined }); } assert.throws(f11, /Unknown option: zorchSplat/); } /** * Test a few cases where no data events should be emitted. */ function noDataEvents() { for (var i = 0; i < 10; i++) { tryWith(i); } function tryWith(count) { var blips = []; for (var i = 0; i < count; i++) { var blip = new pipette.Blip(); blips.push(blip); } var cat = new Cat(blips, { paused: true }); var coll = new EventCollector(); for (var i = 0; i < count; i++) { blips[i].resume(); } coll.listenAllCommon(cat); cat.resume(); assert.equal(coll.events.length, 2); coll.assertEvent(0, cat, "end"); coll.assertEvent(1, cat, "close"); } } /** * Test the basic event sequence / sequencing. */ function basicEventSequence() { for (var i = 1; i < 10; i++) { tryWith(i); } function tryWith(count) { var blips = []; for (var i = 0; i < count; i++) { blips.push(new pipette.Blip(makeData(i))); } var cat = new Cat(blips, { paused: true }); var coll = new EventCollector(); for (var i = 0; i < count; i++) { blips[i].resume(); } coll.listenAllCommon(cat); cat.resume(); assert.equal(coll.events.length, count + 2); for (var i = 0; i < count; i++) { coll.assertEvent(i, cat, "data", [makeData(i)]); } coll.assertEvent(count, cat, "end"); coll.assertEvent(count + 1, cat, "close"); } function makeData(num) { return new Buffer("" + num); } } /** * Test the basic event sequence / sequencing, where an error ends the * event stream. */ function basicErrorEventSequence() { for (var i = 0; i < 10; i++) { tryWith(i); } function tryWith(errorAt) { var blips = []; var theError = new Error("oy"); for (var i = 0; i < 10; i++) { if (i == errorAt) { blips.push(makeErrorBlip(theError)); } else { blips.push(new pipette.Blip(makeData(i))); } } var cat = new Cat(blips, { paused: true }); var coll = new EventCollector(); for (var i = 0; i < blips.length; i++) { blips[i].resume(); } coll.listenAllCommon(cat); cat.resume(); assert.equal(coll.events.length, errorAt + 2); for (var i = 0; i < errorAt; i++) { coll.assertEvent(i, cat, "data", [makeData(i)]); } coll.assertEvent(errorAt, cat, "error", [theError]); coll.assertEvent(errorAt + 1, cat, "close"); } function makeData(num) { return new Buffer("" + num); } } /** * Test that `readable` is true before events were emitted and false * afterwards. Also, check that it becomes false after an error. */ function readableTransition() { var cat = new Cat([], { paused: true }); assert.ok(cat.readable); cat.resume(); assert.ok(!cat.readable); var blip = makeErrorBlip(new Error("eek")); var coll = new EventCollector(); cat = new Cat([blip], { paused: true }); coll.listenAllCommon(cat); blip.resume(); assert.ok(cat.readable); cat.resume(); assert.ok(!cat.readable); } /** * Tests that `setEncoding()` operates as expected in terms of baseline * functionality. */ function setEncoding() { var source = new events.EventEmitter(); var cat = new Cat([source]); var coll = new EventCollector(); coll.listenAllCommon(cat); tryWith(undefined); tryWith("ascii"); tryWith("base64"); tryWith("hex"); tryWith("utf8"); function tryWith(name) { var origData = new Buffer("muffintastic"); var expectPayload; if (name) { expectPayload = origData.toString(name); } else { expectPayload = origData; } cat.setEncoding(name); source.emit("data", origData); assert.equal(coll.events.length, 1); coll.assertEvent(0, cat, "data", [expectPayload]); coll.reset(); } } /** * Tests that the outgoing encoding (set by `setEncoding()`) takes effect * at the time of emission, not at the time of upstream event receipt. */ function setEncodingTiming() { var theData = new Buffer("scones"); var source1 = new events.EventEmitter(); var source2 = new events.EventEmitter(); var cat = new Cat([source1, source2]); var coll = new EventCollector(); coll.listenAllCommon(cat); cat.pause(); source1.emit("data", theData); source2.emit("data", theData); source1.emit("end"); cat.setEncoding("hex"); cat.resume(); assert.equal(coll.events.length, 2); coll.assertEvent(0, cat, "data", [theData.toString("hex")]); coll.assertEvent(1, cat, "data", [theData.toString("hex")]); } /** * Tests that `setIncomingEncoding()` operates as expected in terms of * baseline functionality. */ function setIncomingEncoding() { var source = new events.EventEmitter(); var cat = new Cat([source]); var coll = new EventCollector(); coll.listenAllCommon(cat); tryWith(undefined); tryWith("ascii"); tryWith("base64"); tryWith("hex"); tryWith("utf8"); function tryWith(name) { var origData = new Buffer("biscuitastic"); var emitData; if (name) { emitData = origData.toString(name); } else { emitData = origData; } cat.setIncomingEncoding(name); source.emit("data", emitData); assert.equal(coll.events.length, 1); coll.assertEvent(0, cat, "data", [origData]); coll.reset(); } } /** * Tests that the incoming encoding takes effect at the time of * upstream event receipt, not at the time of downstream emission. */ function setIncomingEncodingTiming() { var theData = new Buffer("croissants"); var source1 = new events.EventEmitter(); var source2 = new events.EventEmitter(); var cat = new Cat([source1, source2]); var coll = new EventCollector(); coll.listenAllCommon(cat); cat.pause(); cat.setIncomingEncoding("base64"); source2.emit("data", theData.toString("base64")); cat.setIncomingEncoding("hex"); source1.emit("data", theData.toString("hex")); source1.emit("end"); cat.setIncomingEncoding("utf8"); cat.resume(); assert.equal(coll.events.length, 2); coll.assertEvent(0, cat, "data", [theData]); coll.assertEvent(1, cat, "data", [theData]); } /** * Tests the common constructor options. */ function commonOptions() { var theData = new Buffer("muffinberry biscuit"); var source = new events.EventEmitter(); var cat = new Cat([ source ], { encoding: "base64", incomingEncoding: "hex", paused: true }); var coll = new EventCollector(); coll.listenAllCommon(cat); source.emit("data", theData.toString("hex")); source.emit("end"); source.emit("close"); assert.ok(cat.readable); assert.equal(coll.events.length, 0); cat.resume(); assert.ok(!cat.readable); assert.equal(coll.events.length, 3); coll.assertEvent(0, cat, "data", [theData.toString("base64")]); coll.assertEvent(1, cat, "end"); coll.assertEvent(2, cat, "close"); } /** * Ensure that no events get passed after a `destroy()` call. */ function afterDestroy() { var cat = new Cat([], { paused: true }); var coll = new EventCollector(); coll.listenAllCommon(cat); cat.destroy(); assert.equal(coll.events.length, 0); cat.resume(); assert.equal(coll.events.length, 0); } function test() { constructor(); constructorFailure(); noDataEvents(); basicEventSequence(); basicErrorEventSequence(); readableTransition(); setEncoding(); setEncodingTiming(); setIncomingEncoding(); setIncomingEncodingTiming(); commonOptions(); afterDestroy(); } module.exports = { test: test };