diff options
author | Alexander Early <alexander.early@gmail.com> | 2018-04-14 20:10:29 -0700 |
---|---|---|
committer | Alexander Early <alexander.early@gmail.com> | 2018-04-14 20:10:29 -0700 |
commit | 45e2938cfa536fe27898c60a4e0fe37080c880bb (patch) | |
tree | c0cb0776b596908d1a79f77c1a30a138dcba6a00 /mocha_test | |
parent | 2030f5f1bd01a141ac4c85d3deab927f49e255fc (diff) | |
parent | 3235c8f5f67ff38b85ce6f9664053d0b1897ff3b (diff) | |
download | async-45e2938cfa536fe27898c60a4e0fe37080c880bb.tar.gz |
Merge branch 'master' into DELETE_THIS_BRANCHDELETE_THIS_BRANCH
Diffstat (limited to 'mocha_test')
-rw-r--r-- | mocha_test/applyEach.js | 6 | ||||
-rw-r--r-- | mocha_test/asyncFunctions.js | 13 | ||||
-rw-r--r-- | mocha_test/asyncify.js | 38 | ||||
-rw-r--r-- | mocha_test/cargo.js | 55 | ||||
-rw-r--r-- | mocha_test/concat.js | 442 | ||||
-rw-r--r-- | mocha_test/eachOf.js | 60 | ||||
-rw-r--r-- | mocha_test/es2017/asyncFunctions.js | 20 | ||||
-rw-r--r-- | mocha_test/groupBy.js | 2 | ||||
-rw-r--r-- | mocha_test/linked_list.js | 83 | ||||
-rw-r--r-- | mocha_test/queue.js | 237 | ||||
-rw-r--r-- | mocha_test/retry.js | 23 | ||||
-rw-r--r-- | mocha_test/slice.js | 32 | ||||
-rw-r--r-- | mocha_test/timeout.js | 36 | ||||
-rw-r--r-- | mocha_test/tryEach.js | 86 | ||||
-rw-r--r-- | mocha_test/waterfall.js | 32 |
15 files changed, 1003 insertions, 162 deletions
diff --git a/mocha_test/applyEach.js b/mocha_test/applyEach.js index 6138d3f..ca5ada3 100644 --- a/mocha_test/applyEach.js +++ b/mocha_test/applyEach.js @@ -11,21 +11,21 @@ describe('applyEach', function () { setTimeout(function () { call_order.push('one'); cb(null, 1); - }, 10); + }, 12); }; var two = function (val, cb) { expect(val).to.equal(5); setTimeout(function () { call_order.push('two'); cb(null, 2); - }, 5); + }, 2); }; var three = function (val, cb) { expect(val).to.equal(5); setTimeout(function () { call_order.push('three'); cb(null, 3); - }, 15); + }, 18); }; async.applyEach([one, two, three], 5, function (err, results) { assert(err === null, err + " passed instead of 'null'"); diff --git a/mocha_test/asyncFunctions.js b/mocha_test/asyncFunctions.js index b756a90..3b759e1 100644 --- a/mocha_test/asyncFunctions.js +++ b/mocha_test/asyncFunctions.js @@ -1,4 +1,15 @@ -var supportsAsync = require('../lib/internal/wrapAsync').supportsAsync; +var isAsync = require('../lib/internal/wrapAsync').isAsync; + +function supportsAsync() { + var supported; + try { + /* eslint no-eval: 0 */ + supported = isAsync(eval('(async function () {})')); + } catch (e) { + supported = false; + } + return supported; +} describe('async function support', function () { this.timeout(100); diff --git a/mocha_test/asyncify.js b/mocha_test/asyncify.js index a98826c..112b8ad 100644 --- a/mocha_test/asyncify.js +++ b/mocha_test/asyncify.js @@ -92,7 +92,9 @@ describe('asyncify', function(){ }); }); - it('callback error', function(done) { + it('callback error @nodeonly', function(done) { + expectUncaughtException(); + var promisified = function(argument) { return new Promise(function (resolve) { resolve(argument + " resolved"); @@ -105,11 +107,30 @@ describe('asyncify', function(){ throw new Error("error in callback"); } }); + setTimeout(function () { expect(call_count).to.equal(1); done(); }, 15); }); + + it('dont catch errors in the callback @nodeonly', function(done) { + expectUncaughtException(checkErr); + var callbackError = new Error('thrown from callback'); + + function checkErr(err) { + expect(err).to.equal(callbackError); + done(); + } + + function callback() { + throw callbackError; + } + + async.asyncify(function () { + return Promise.reject(new Error('rejection')); + })(callback); + }); } describe('native-promise-only', function() { @@ -134,5 +155,20 @@ describe('asyncify', function(){ var Promise = require('rsvp').Promise; promisifiedTests.call(this, Promise); }); + + function expectUncaughtException(onError) { + // do a weird dance to catch the async thrown error before mocha + var listeners = process.listeners('uncaughtException'); + process.removeAllListeners('uncaughtException'); + process.once('uncaughtException', function onErr(err) { + listeners.forEach(function(listener) { + process.on('uncaughtException', listener); + }); + // can't throw errors in a uncaughtException handler, defer + if (onError) { + setTimeout(onError, 0, err); + } + }); + } }); }); diff --git a/mocha_test/cargo.js b/mocha_test/cargo.js index 235b9a2..55e6e96 100644 --- a/mocha_test/cargo.js +++ b/mocha_test/cargo.js @@ -236,7 +236,7 @@ describe('cargo', function () { it('expose payload', function (done) { var called_once = false; - var cargo= async.cargo(function(tasks, cb) { + var cargo = async.cargo(function(tasks, cb) { if (!called_once) { expect(cargo.payload).to.equal(1); assert(tasks.length === 1, 'should start with payload = 1'); @@ -261,4 +261,57 @@ describe('cargo', function () { }, 15); }); + it('workersList', function(done) { + var called_once = false; + + function getWorkersListData(cargo) { + return cargo.workersList().map(function(v) { + return v.data; + }); + } + + var cargo = async.cargo(function(tasks, cb) { + if (!called_once) { + expect(tasks).to.eql(['foo', 'bar']); + } else { + expect(tasks).to.eql(['baz']); + } + expect(getWorkersListData(cargo)).to.eql(tasks); + async.setImmediate(function() { + // ensure nothing has changed + expect(getWorkersListData(cargo)).to.eql(tasks); + called_once = true; + cb(); + }); + }, 2); + + cargo.drain = function() { + expect(cargo.workersList()).to.eql([]); + expect(cargo.running()).to.equal(0); + done(); + }; + + cargo.push('foo'); + cargo.push('bar'); + cargo.push('baz'); + }); + + it('running', function(done) { + var cargo = async.cargo(function(tasks, cb) { + expect(cargo.running()).to.equal(1); + async.setImmediate(function() { + expect(cargo.running()).to.equal(1); + cb(); + }); + }, 2); + + cargo.drain = function() { + expect(cargo.running()).to.equal(0); + done(); + }; + + cargo.push('foo'); + cargo.push('bar'); + cargo.push('baz'); + }) }); diff --git a/mocha_test/concat.js b/mocha_test/concat.js index 389b2de..f6b73b3 100644 --- a/mocha_test/concat.js +++ b/mocha_test/concat.js @@ -3,55 +3,415 @@ var expect = require('chai').expect; var assert = require('assert'); describe('concat', function() { - it('concat', function(done) { - var call_order = []; - var iteratee = function (x, cb) { - setTimeout(function(){ - call_order.push(x); - var r = []; - while (x > 0) { - r.push(x); - x--; + this.timeout(250); + + function concatIteratee(callOrder, val, next) { + setTimeout(function() { + callOrder.push(val); + next(null, [val, val+1]); + }, val * 25); + } + + context('concat', function() { + it('basics', function(done) { + var callOrder = []; + async.concat([1, 3, 2], concatIteratee.bind(this, callOrder), function(err, result) { + expect(err).to.eql(null); + expect(callOrder).to.eql([1, 2, 3]); + expect(result).to.eql([1, 2, 3, 4, 2, 3]); + done(); + }); + }); + + it('error', function(done) { + async.concat([1, 3, 2], function(val, next) { + if (val === 3) { + return next(new Error('fail')); } - cb(null, r); - }, x*25); - }; - async.concat([1,3,2], iteratee, function(err, results){ - expect(results).to.eql([1,2,1,3,2,1]); - expect(call_order).to.eql([1,2,3]); - assert(err === null, err + " passed instead of 'null'"); - done(); + next(null, [val, val+1]); + }, function(err, result) { + expect(err).to.not.eql(null); + expect(result).to.eql([1, 2]); + done(); + }); + }); + + it('original untouched', function(done) { + var arr = ['foo', 'bar', 'baz']; + async.concat(arr, function(val, next) { + next(null, [val, val]); + }, function(err, result) { + expect(arr).to.eql(['foo', 'bar', 'baz']); + expect(result).to.eql(['foo', 'foo', 'bar', 'bar', 'baz', 'baz']); + done(); + }); + }); + + it('empty results', function(done) { + var arr = ['foo', 'bar', 'baz']; + async.concat(arr, function(val, next) { + next(null); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.be.an('array').that.is.empty; + done(); + }); + }); + + it('empty arrays', function(done) { + var arr = ['foo', 'bar', 'baz']; + async.concat(arr, function(val, next) { + next(null, []); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.be.an('array').that.is.empty; + done(); + }); + }); + + it('handles empty object', function(done) { + async.concat({}, function(val, next) { + assert(false, 'iteratee should not be called'); + next(); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.be.an('array').that.is.empty; + done(); + }); + }); + + it('variadic', function(done) { + var arr = ['foo', 'bar', 'baz']; + async.concat(arr, function(val, next) { + next(null, val, val); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql(['foo', 'foo', 'bar', 'bar', 'baz', 'baz']); + done(); + }); + }); + + it('flattens arrays', function(done) { + var arr = ['foo', 'bar']; + async.concat(arr, function(val, next) { + next(null, [val, [val]]); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql(['foo', ['foo'], 'bar', ['bar']]); + done(); + }); + }); + + it('handles fasly values', function(done) { + var falsy = [null, undefined, 0, '']; + async.concat(falsy, function(val, next) { + next(null, val); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql(falsy); + done(); + }); + }); + + it('handles objects', function(done) { + var obj = {a: 'foo', b: 'bar', c: 'baz'}; + async.concat(obj, function(val, next) { + next(null, val); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql(['foo', 'bar', 'baz']); + done(); + }); + }); + + it('main callback optional', function(done) { + var arr = [1, 2, 3]; + var runs = []; + async.concat(arr, function(val, next) { + runs.push(val); + var _done = (runs.length === arr.length); + async.setImmediate(function() { + next(null); + if (_done) { + expect(runs).to.eql(arr); + done(); + } + }); + }); + }); + + it('iteratee callback is only called once', function(done) { + async.concat([1, 2], function(val, next) { + try { + next(val); + } catch (exception) { + expect(function() { + next(exception); + }).to.throw(/already called/); + done(); + } + }, function() { + throw new Error(); + }); + }); + + it('preserves order', function(done) { + var arr = [30, 15]; + async.concat(arr, function(x, cb) { + setTimeout(function() { + cb(null, x); + }, x); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql(arr); + done(); + }); + }); + + it('handles Map', function(done) { + if (typeof Map !== 'function') { + return done(); + } + + var map = new Map([ + ['a', 'b'], + ['b', 'c'], + ['c', 'd'] + ]); + + async.concat(map, function(val, next) { + next(null, val); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql(['a', 'b', 'b', 'c', 'c', 'd']); + done(); + }); + }); + + it('handles sparse results', function(done) { + var arr = [1, 2, 3, 4]; + async.concat(arr, function(val, next) { + if (val === 1 || val === 3) { + return next(null, val+1); + } else if (val === 2) { + async.setImmediate(function() { + return next(null, val+1); + }); + } else { + return next('error'); + } + }, function(err, result) { + expect(err).to.not.eql(null); + expect(result).to.eql([2, 4]); + async.setImmediate(done); + }); }); }); - it('concat error', function(done) { - var iteratee = function (x, cb) { - cb(new Error('test error')); - }; - async.concat([1,2,3], iteratee, function(err){ - assert(err); - done(); + context('concatLimit', function() { + var arr = ['foo', 'bar', 'baz']; + it('basics', function(done) { + var running = 0; + var concurrency = {'foo': 2, 'bar': 2, 'baz': 1}; + async.concatLimit(arr, 2, function(val, next) { + running++; + async.setImmediate(function() { + expect(running).to.equal(concurrency[val]); + running--; + next(null, val, val); + }) + }, function(err, result) { + expect(running).to.equal(0); + expect(err).to.eql(null); + expect(result).to.eql(['foo', 'foo', 'bar', 'bar', 'baz', 'baz']); + done(); + }); + }); + + it('error', function(done) { + async.concatLimit(arr, 1, function(val, next) { + if (val === 'bar') { + return next(new Error('fail')); + } + next(null, val); + }, function(err, result) { + expect(err).to.not.eql(null); + expect(result).to.eql(['foo']); + done(); + }); + }); + + it('handles objects', function(done) { + async.concatLimit({'foo': 1, 'bar': 2, 'baz': 3}, 2, function(val, next) { + next(null, val+1); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql([2, 3, 4]); + done(); + }); + }); + + it('handles empty object', function(done) { + async.concatLimit({}, 2, function(val, next) { + assert(false, 'iteratee should not be called'); + next(); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.be.an('array').that.is.empty; + done(); + }); + }); + + it('handles undefined', function(done) { + async.concatLimit(undefined, 2, function(val, next) { + assert(false, 'iteratee should not be called'); + next(); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.be.an('array').that.is.empty; + done(); + }); + }); + + it('limit exceeds size', function(done) { + var callOrder = []; + async.concatLimit([3, 2, 2, 1], 10, concatIteratee.bind(this, callOrder), function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql([3, 4, 2, 3, 2, 3, 1, 2]); + expect(callOrder).to.eql([1, 2, 2, 3]); + done(); + }); + }); + + it('limit equal size', function(done) { + var callOrder = []; + async.concatLimit([3, 2, 2, 1], 4, concatIteratee.bind(this, callOrder), function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql([3, 4, 2, 3, 2, 3, 1, 2]); + expect(callOrder).to.eql([1, 2, 2, 3]); + done(); + }); + }); + + it('zero limit', function(done) { + async.concatLimit([3, 2, 2, 1], 0, function(val, next) { + assert(false, 'iteratee should not be called'); + next(); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.be.an('array').that.is.empty; + done(); + }); + }); + + it('does not continue replenishing after error', function(done) { + var started = 0; + var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + var limit = 3; + var step = 0; + var maxSteps = arr.length; + + async.concatLimit(arr, limit, function(val, next) { + started++; + if (started === 3) { + return next(new Error('fail')); + } + + async.setImmediate(function() { + next(); + }); + }, function(err, result) { + expect(err).to.not.eql(null); + expect(result).to.be.an('array').that.is.empty; + }); + + // wait `maxSteps` event loop cycles before calling done to ensure + // the iteratee is not called on more items in arr. + function waitCycle() { + step++; + if (step >= maxSteps) { + expect(started).to.equal(3); + done(); + return; + } else { + async.setImmediate(waitCycle); + } + } + + async.setImmediate(waitCycle); }); }); - it('concatSeries', function(done) { - var call_order = []; - var iteratee = function (x, cb) { - setTimeout(function(){ - call_order.push(x); - var r = []; - while (x > 0) { - r.push(x); - x--; + context('concatSeries', function() { + it('basics', function(done) { + var callOrder = []; + var running = 0; + var iteratee = function (x, cb) { + running++; + setTimeout(function() { + expect(running).to.equal(1); + running--; + callOrder.push(x); + var r = []; + while (x > 0) { + r.push(x); + x--; + } + cb(null, r); + }, x*25); + }; + async.concatSeries([1,3,2], iteratee, function(err, results) { + expect(results).to.eql([1,3,2,1,2,1]); + expect(running).to.equal(0); + expect(callOrder).to.eql([1,3,2]); + assert(err === null, err + " passed instead of 'null'"); + done(); + }); + }); + + it('error', function(done) { + async.concatSeries(['foo', 'bar', 'baz'], function(val, next) { + if (val === 'bar') { + return next(new Error('fail')); } - cb(null, r); - }, x*25); - }; - async.concatSeries([1,3,2], iteratee, function(err, results){ - expect(results).to.eql([1,3,2,1,2,1]); - expect(call_order).to.eql([1,3,2]); - assert(err === null, err + " passed instead of 'null'"); - done(); + next(null, [val, val]); + }, function(err, result) { + expect(err).to.not.eql(null); + expect(result).to.eql(['foo', 'foo']); + done(); + }); + }); + + it('handles objects', function(done) { + async.concatSeries({'foo': 1, 'bar': 2, 'baz': 3}, function(val, next) { + return next(null, [val, val+1]); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.eql([1, 2, 2, 3, 3, 4]); + done(); + }); + }); + + it('handles empty object', function(done) { + async.concatSeries({}, function(val, next) { + assert(false, 'iteratee should not be called'); + next(); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.be.an('array').that.is.empty; + done(); + }); + }); + + it('handles undefined', function(done) { + async.concatSeries(undefined, function(val, next) { + assert(false, 'iteratee should not be called'); + next(); + }, function(err, result) { + expect(err).to.eql(null); + expect(result).to.be.an('array').that.is.empty; + done(); + }); }); }); }); diff --git a/mocha_test/eachOf.js b/mocha_test/eachOf.js index 925f995..dd73c22 100644 --- a/mocha_test/eachOf.js +++ b/mocha_test/eachOf.js @@ -43,6 +43,30 @@ describe("eachOf", function() { }); }); + it('forEachOf no call stack size exceed error', function(done) { + var obj = {}; + var len = 3000; + var args = new Array(len * 2); + var expected = new Array(len * 2); + + for (var i = 0; i < len; i++) { + obj["a" + i] = i; + expected[2 * i] = "a" + i; + expected[2 * i + 1] = i; + } + + async.forEachOf(obj, function(value, key, callback) { + var index = parseInt(key.slice(1), 10); + args[2 * index] = key; + args[2 * index + 1] = value; + callback(); + }, function(err) { + assert(err === null, err + " passed instead of 'null'"); + expect(args).to.eql(expected); + done(); + }); + }); + it('forEachOf - instant resolver', function(done) { var args = []; async.forEachOf({ a: 1, b: 2 }, function(x, k, cb) { @@ -139,6 +163,30 @@ describe("eachOf", function() { }); }); + it('forEachOfSeries no call stack size exceed error', function(done) { + var obj = {}; + var len = 3000; + var args = new Array(len * 2); + var expected = new Array(len * 2); + + for (var i = 0; i < len; i++) { + obj["a" + i] = i; + expected[2 * i] = "a" + i; + expected[2 * i + 1] = i; + } + + async.forEachOfSeries(obj, function(value, key, callback) { + var index = parseInt(key.slice(1), 10); + args[2 * index] = key; + args[2 * index + 1] = value; + callback(); + }, function(err) { + assert(err === null, err + " passed instead of 'null'"); + expect(args).to.eql(expected); + done(); + }); + }); + it('forEachOfSeries empty object', function(done) { async.forEachOfSeries({}, function(x, callback){ assert(false, 'iteratee should not be called'); @@ -274,6 +322,18 @@ describe("eachOf", function() { setTimeout(done, 25); }); + it('forEachOfLimit no call stack size exceed error', function(done) { + var count = 0; + async.forEachOfLimit(_.range(1024 * 1024), Infinity, function(x, i, callback){ + count++; + callback(); + }, function(err){ + if (err) throw err; + expect(count).to.equal(1024 * 1024); + done(); + }); + }); + it('forEachOfLimit error', function(done) { var obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }; var call_order = []; diff --git a/mocha_test/es2017/asyncFunctions.js b/mocha_test/es2017/asyncFunctions.js index 8f77bdc..5616307 100644 --- a/mocha_test/es2017/asyncFunctions.js +++ b/mocha_test/es2017/asyncFunctions.js @@ -219,6 +219,14 @@ module.exports = function () { }); }); + it('should handle async functions in concatLimit', (done) => { + async.concatLimit(input, 2, asyncIdentity, (err, result) => { + expect(err).to.eql(null); + expect(result).to.eql(input); + done(err); + }); + }); + it('should handle async functions in concatSeries', (done) => { async.concatSeries(input, asyncIdentity, (err, result) => { expect(result).to.eql(input); @@ -622,6 +630,18 @@ module.exports = function () { }) }); + it('should handle async functons in tryEach', (done) => { + async.tryEach([ + async () => { throw new Error('fail1'); }, + async () => { throw new Error('fail2'); }, + async () => 5, + async () => { throw new Error('shoult not get here'); } + ], (err, result) => { + expect(result).to.eql(5); + done(); + }) + }); + /** * Utils */ diff --git a/mocha_test/groupBy.js b/mocha_test/groupBy.js index afb612b..d20f385 100644 --- a/mocha_test/groupBy.js +++ b/mocha_test/groupBy.js @@ -330,7 +330,7 @@ describe('groupBy', function() { }); it('handles empty object', function(done) { - async.groupByLimit({}, 2, function(val, next) { + async.groupBySeries({}, function(val, next) { assert(false, 'iteratee should not be called'); next(); }, function(err, result) { diff --git a/mocha_test/linked_list.js b/mocha_test/linked_list.js new file mode 100644 index 0000000..ab4b223 --- /dev/null +++ b/mocha_test/linked_list.js @@ -0,0 +1,83 @@ +var DLL = require('../lib/internal/DoublyLinkedList').default; +var expect = require('chai').expect; + +describe('DoublyLinkedList', function () { + it('toArray', function() { + var list = new DLL(); + expect(list.toArray()).to.eql([]); + + for (var i = 0; i < 5; i++) { + list.push({data: i}); + } + expect(list.toArray()).to.eql([0, 1, 2, 3, 4]); + }); + + it('remove', function() { + var list = new DLL(); + + for (var i = 0; i < 5; i++) { + list.push({data: i}); + } + + list.remove(function (node) { + return node.data === 3; + }) + + expect(list.toArray()).to.eql([0, 1, 2, 4]); + }); + + it('remove (head)', function() { + var list = new DLL(); + + for (var i = 0; i < 5; i++) { + list.push({data: i}); + } + + list.remove(function (node) { + return node.data === 0; + }) + + expect(list.toArray()).to.eql([1, 2, 3, 4]); + }); + + it('remove (tail)', function() { + var list = new DLL(); + + for (var i = 0; i < 5; i++) { + list.push({data: i}); + } + + list.remove(function (node) { + return node.data === 4; + }) + + expect(list.toArray()).to.eql([0, 1, 2, 3]); + }); + + it('remove (all)', function() { + var list = new DLL(); + + for (var i = 0; i < 5; i++) { + list.push({data: i}); + } + + list.remove(function (node) { + return node.data < 5; + }) + + expect(list.toArray()).to.eql([]); + }); + + it('empty', function() { + var list = new DLL(); + + for (var i = 0; i < 5; i++) { + list.push({data: i}); + } + + var empty = list.empty(); + + expect(list).to.equal(empty); + expect(list.toArray()).to.eql([]); + }); +}); diff --git a/mocha_test/queue.js b/mocha_test/queue.js index cc72c52..4539c14 100644 --- a/mocha_test/queue.js +++ b/mocha_test/queue.js @@ -10,7 +10,7 @@ describe('queue', function(){ it('basics', function(done) { var call_order = []; - var delays = [40,20,60,20]; + var delays = [40,10,60,10]; // worker1: --1-4 @@ -66,7 +66,7 @@ describe('queue', function(){ it('default concurrency', function(done) { var call_order = [], - delays = [40,20,60,20]; + delays = [40,10,60,10]; // order of completion: 1,2,3,4 @@ -222,16 +222,21 @@ describe('queue', function(){ it('push without callback', function(done) { this.retries(3); // test can be flakey - var call_order = [], - delays = [40,20,60,20]; + var call_order = []; + var delays = [40,10,60,10]; + var concurrencyList = []; + var running = 0; // worker1: --1-4 // worker2: -2---3 // order of completion: 2,1,4,3 var q = async.queue(function (task, callback) { + running++; + concurrencyList.push(running); setTimeout(function () { call_order.push('process ' + task); + running--; callback('error', 'arg'); }, delays.shift()); }, 2); @@ -242,6 +247,8 @@ describe('queue', function(){ q.push(4); q.drain = function () { + expect(running).to.eql(0); + expect(concurrencyList).to.eql([1, 2, 2, 2]); expect(call_order).to.eql([ 'process 2', 'process 1', @@ -293,7 +300,7 @@ describe('queue', function(){ it('bulk task', function(done) { var call_order = [], - delays = [40,20,60,20]; + delays = [40,10,60,10]; // worker1: --1-4 // worker2: -2---3 @@ -353,58 +360,58 @@ describe('queue', function(){ }); it('pause', function(done) { - this.retries(3); // sometimes can be flakey to timing issues - - var call_order = [], - task_timeout = 80, - pause_timeout = task_timeout * 2.5, - resume_timeout = task_timeout * 4.5, - tasks = [ 1, 2, 3, 4, 5, 6 ], - - elapsed = (function () { - var start = Date.now(); - return function () { - return Math.round((Date.now() - start) / task_timeout) * task_timeout; - }; - })(); + var call_order = []; + var running = 0; + var concurrencyList = []; + var pauseCalls = ['process 1', 'process 2', 'process 3']; var q = async.queue(function (task, callback) { + running++; call_order.push('process ' + task); - call_order.push('timeout ' + elapsed()); - callback(); - }); - - function pushTask () { - var task = tasks.shift(); - if (!task) { return; } + concurrencyList.push(running); setTimeout(function () { - q.push(task); - pushTask(); - }, task_timeout); - } - pushTask(); + running--; + callback(); + }, 10) + }, 2); - setTimeout(function () { + q.push(1); + q.push(2, after2); + q.push(3); + + function after2() { q.pause(); - expect(q.paused).to.equal(true); - }, pause_timeout); + expect(concurrencyList).to.eql([1, 2, 2]); + expect(call_order).to.eql(pauseCalls); - setTimeout(function () { - q.resume(); - expect(q.paused).to.equal(false); - }, resume_timeout); + setTimeout(whilePaused, 5); + setTimeout(afterPause, 10); + } - setTimeout(function () { + function whilePaused() { + q.push(4); + } + + function afterPause() { + expect(concurrencyList).to.eql([1, 2, 2]); + expect(call_order).to.eql(pauseCalls); + q.resume(); + q.push(5); + q.push(6); + q.drain = drain; + } + function drain () { + expect(concurrencyList).to.eql([1, 2, 2, 1, 2, 2]); expect(call_order).to.eql([ - 'process 1', 'timeout ' + task_timeout, - 'process 2', 'timeout ' + task_timeout * 2, - 'process 3', 'timeout ' + task_timeout * 5, - 'process 4', 'timeout ' + task_timeout * 5, - 'process 5', 'timeout ' + task_timeout * 5, - 'process 6', 'timeout ' + task_timeout * 6 + 'process 1', + 'process 2', + 'process 3', + 'process 4', + 'process 5', + 'process 6' ]); done(); - }, (task_timeout * tasks.length) + pause_timeout + resume_timeout); + } }); it('pause in worker with concurrency', function(done) { @@ -436,57 +443,6 @@ describe('queue', function(){ }; }); - it('pause with concurrency', function(done) { - var call_order = [], - task_timeout = 40, - pause_timeout = task_timeout / 2, - resume_timeout = task_timeout * 2.75, - tasks = [ 1, 2, 3, 4, 5, 6 ], - - elapsed = (function () { - var start = Date.now(); - return function () { - return Math.round((Date.now() - start) / task_timeout) * task_timeout; - }; - })(); - - var q = async.queue(function (task, callback) { - setTimeout(function () { - call_order.push('process ' + task); - call_order.push('timeout ' + elapsed()); - callback(); - }, task_timeout); - }, 2); - - q.push(tasks); - - setTimeout(function () { - q.pause(); - expect(q.paused).to.equal(true); - }, pause_timeout); - - setTimeout(function () { - q.resume(); - expect(q.paused).to.equal(false); - }, resume_timeout); - - setTimeout(function () { - expect(q.running()).to.equal(2); - }, resume_timeout + 10); - - setTimeout(function () { - expect(call_order).to.eql([ - 'process 1', 'timeout ' + task_timeout, - 'process 2', 'timeout ' + task_timeout, - 'process 3', 'timeout ' + task_timeout * 4, - 'process 4', 'timeout ' + task_timeout * 4, - 'process 5', 'timeout ' + task_timeout * 5, - 'process 6', 'timeout ' + task_timeout * 5 - ]); - done(); - }, (task_timeout * tasks.length) + pause_timeout + resume_timeout); - }); - it('start paused', function(done) { var q = async.queue(function (task, callback) { setTimeout(function () { @@ -498,11 +454,12 @@ describe('queue', function(){ q.push([1, 2, 3]); setTimeout(function () { + expect(q.running()).to.equal(0); q.resume(); }, 5); setTimeout(function () { - expect(q._tasks.length).to.equal(1); + expect(q.length()).to.equal(1); expect(q.running()).to.equal(2); q.resume(); }, 15); @@ -699,7 +656,7 @@ describe('queue', function(){ }); }); - context('q.unsaturated(): ',function() { + context('q.unsaturated(): ', function() { it('should have a default buffer property that equals 25% of the concurrenct rate', function(done){ var calls = []; var q = async.queue(function(task, cb) { @@ -761,5 +718,85 @@ describe('queue', function(){ q.push('foo4', function () {calls.push('foo4 cb');}); }); }); -}); + context('workersList', function() { + it('should be the same length as running()', function(done) { + var q = async.queue(function(task, cb) { + async.setImmediate(function() { + expect(q.workersList().length).to.equal(q.running()); + cb(); + }); + }, 2); + + q.drain = function() { + expect(q.workersList().length).to.equal(0); + expect(q.running()).to.equal(0); + done(); + }; + + q.push('foo'); + q.push('bar'); + q.push('baz'); + }); + + it('should contain the items being processed', function(done) { + var itemsBeingProcessed = { + 'foo': ['foo'], + 'foo_cb': ['foo', 'bar'], + 'bar': ['foo', 'bar'], + 'bar_cb': ['bar', 'baz'], + 'baz': ['bar', 'baz'], + 'baz_cb': ['baz'] + }; + + function getWorkersListData(q) { + return q.workersList().map(function(v) { + return v.data; + }); + } + + var q = async.queue(function(task, cb) { + expect( + getWorkersListData(q) + ).to.eql(itemsBeingProcessed[task]); + expect(q.workersList().length).to.equal(q.running()); + async.setImmediate(function() { + expect( + getWorkersListData(q) + ).to.eql(itemsBeingProcessed[task+'_cb']); + expect(q.workersList().length).to.equal(q.running()); + cb(); + }); + }, 2); + + q.drain = function() { + expect(q.workersList()).to.eql([]); + expect(q.workersList().length).to.equal(q.running()); + done(); + }; + + q.push('foo'); + q.push('bar'); + q.push('baz'); + }); + }) + + it('remove', function(done) { + var result = []; + var q = async.queue(function(data, cb) { + result.push(data); + async.setImmediate(cb); + }); + + q.push([1, 2, 3, 4, 5]); + + q.remove(function (node) { + return node.data === 3; + }); + + q.drain = function () { + expect(result).to.eql([1, 2, 4, 5]); + done(); + } + }); +}); diff --git a/mocha_test/retry.js b/mocha_test/retry.js index d3a5d22..8a9f4da 100644 --- a/mocha_test/retry.js +++ b/mocha_test/retry.js @@ -66,11 +66,10 @@ describe("retry", function () { callCount++; callback(error + callCount, erroredResult + callCount); // respond with indexed values } - var start = new Date().getTime(); + var start = Date.now(); async.retry({ times: times, interval: interval}, fn, function(err, result){ - var now = new Date().getTime(); - var duration = now - start; - assert(duration >= (interval * (times -1)), 'did not include interval'); + var duration = Date.now() - start; + expect(duration).to.be.above(interval * (times - 1) - times); assert.equal(callCount, 3, "did not retry the correct number of times"); assert.equal(err, error + times, "Incorrect error was returned"); assert.equal(result, erroredResult + times, "Incorrect result was returned"); @@ -88,11 +87,10 @@ describe("retry", function () { callCount++; callback(error + callCount, erroredResult + callCount); // respond with indexed values } - var start = new Date().getTime(); + var start = Date.now(); async.retry({ times: times, interval: intervalFunc}, fn, function(err, result){ - var now = new Date().getTime(); - var duration = now - start; - assert(duration >= 300, 'did not include custom interval'); + var duration = Date.now() - start; + expect(duration).to.be.above(300 - times); assert.equal(callCount, 3, "did not retry the correct number of times"); assert.equal(err, error + times, "Incorrect error was returned"); assert.equal(result, erroredResult + times, "Incorrect result was returned"); @@ -127,7 +125,7 @@ describe("retry", function () { it('retry does not precompute the intervals (#1226)', function(done) { var callTimes = []; function intervalFunc() { - callTimes.push(new Date().getTime()); + callTimes.push(Date.now()); return 100; }; function fn(callback) { @@ -226,11 +224,10 @@ describe("retry", function () { function errorTest(err) { return err && err !== special; } - var start = new Date().getTime(); + var start = Date.now(); async.retry({ interval: interval, errorFilter: errorTest }, fn, function(err, result){ - var now = new Date().getTime(); - var duration = now - start; - assert(duration >= (interval * (specialCount - 1)), 'did not include interval'); + var duration = Date.now() - start; + expect(duration).to.be.above(interval * (specialCount - 1) - specialCount); assert.equal(callCount, specialCount, "did not retry the correct number of times"); assert.equal(err, special, "Incorrect error was returned"); assert.equal(result, erroredResult + specialCount, "Incorrect result was returned"); diff --git a/mocha_test/slice.js b/mocha_test/slice.js new file mode 100644 index 0000000..7020526 --- /dev/null +++ b/mocha_test/slice.js @@ -0,0 +1,32 @@ +var slice = require('../lib/internal/slice').default; +var expect = require('chai').expect; + +describe('slice', function() { + it('should slice arrays', function() { + var arr = ['foo', 'bar', 'baz']; + var result = slice(arr, 2); + expect(arr).to.eql(['foo', 'bar', 'baz']); + expect(result).to.eql(['baz']); + }); + + it('should handle ArrayLike objects', function() { + var args = {0: 'foo', 1: 'bar', 2: 'baz', length: 3}; + var result = slice(args, 1); + expect(result).to.be.an('array'); + expect(result).to.eql(['bar', 'baz']); + }); + + it('should handle arguments', function() { + var foo = function() { + return slice(arguments, 1); + }; + var result = foo.apply(null, ['foo', 'bar', 'baz']); + expect(result).to.be.an('array'); + expect(result).to.eql(['bar', 'baz']); + }); + + it('should return an empty array on an invalid start', function() { + var result = slice(['foo', 'bar', 'baz'], 10); + expect(result).to.be.an('array').that.is.empty; + }); +}); diff --git a/mocha_test/timeout.js b/mocha_test/timeout.js index be41283..cd4a751 100644 --- a/mocha_test/timeout.js +++ b/mocha_test/timeout.js @@ -69,4 +69,40 @@ describe('timeout', function () { done(); }); }); + + it('timeout with multiple calls (#1418)', function(done) { + var timeout = async.timeout(function asyncFn(n, callback) { + if (n < 1) { + setTimeout(function() { + callback(null, 'I will time out'); + }, 75); + } else { + async.setImmediate(function() { + callback(null, 'I didn\'t time out'); + }) + } + }, 50); + + async.series([ + function(cb) { + timeout(0, function(err, result) { + expect(err.message).to.equal('Callback function "asyncFn" timed out.'); + expect(err.code).to.equal('ETIMEDOUT'); + expect(err.info).to.equal(undefined); + expect(result).to.equal(undefined); + cb(); + }); + }, + function(cb) { + timeout(1, function(err, result) { + expect(err).to.equal(null); + expect(result).to.equal('I didn\'t time out'); + cb(); + }); + } + ], function(err) { + expect(err).to.equal(null); + done(); + }); + }) }); diff --git a/mocha_test/tryEach.js b/mocha_test/tryEach.js new file mode 100644 index 0000000..db884a5 --- /dev/null +++ b/mocha_test/tryEach.js @@ -0,0 +1,86 @@ +var async = require('../lib'); +var expect = require('chai').expect; +var assert = require('assert'); + +describe('tryEach', function () { + it('no callback', function () { + async.tryEach([]); + }); + it('empty', function (done) { + async.tryEach([], function (err, results) { + expect(err).to.equal(null); + expect(results).to.eql(undefined); + done(); + }); + }); + it('one task, multiple results', function (done) { + var RESULTS = ['something', 'something2']; + async.tryEach([ + function (callback) { + callback(null, RESULTS[0], RESULTS[1]); + } + ], function (err, results) { + expect(err).to.equal(null); + expect(results).to.eql(RESULTS); + done(); + }); + }); + it('one task', function (done) { + var RESULT = 'something'; + async.tryEach([ + function (callback) { + callback(null, RESULT); + } + ], function (err, results) { + expect(err).to.equal(null); + expect(results).to.eql(RESULT); + done(); + }); + }); + it('two tasks, one failing', function (done) { + var RESULT = 'something'; + async.tryEach([ + function (callback) { + callback(new Error('Failure'), {}); + }, + function (callback) { + callback(null, RESULT); + } + ], function (err, results) { + expect(err).to.equal(null); + expect(results).to.eql(RESULT); + done(); + }); + }); + it('two tasks, both failing', function (done) { + var ERROR_RESULT = new Error('Failure2'); + async.tryEach([ + function (callback) { + callback(new Error('Should not stop here')); + }, + function (callback) { + callback(ERROR_RESULT); + } + ], function (err, results) { + expect(err).to.equal(ERROR_RESULT); + expect(results).to.eql(undefined); + done(); + }); + }); + it('two tasks, non failing', function (done) { + var RESULT = 'something'; + async.tryEach([ + function (callback) { + callback(null, RESULT); + }, + function () { + assert.fail('Should not been called'); + }, + ], function (err, results) { + expect(err).to.equal(null); + expect(results).to.eql(RESULT); + done(); + }); + }); +}); + diff --git a/mocha_test/waterfall.js b/mocha_test/waterfall.js index 0c21c80..54ca9f9 100644 --- a/mocha_test/waterfall.js +++ b/mocha_test/waterfall.js @@ -93,7 +93,6 @@ describe("waterfall", function () { it('multiple callback calls', function(){ var arr = [ function(callback){ - // call the callback twice. this should call function 2 twice callback(null, 'one', 'two'); callback(null, 'one', 'two'); }, @@ -106,6 +105,37 @@ describe("waterfall", function () { }).to.throw(/already called/); }); + it('multiple callback calls (trickier) @nodeonly', function(done){ + + // do a weird dance to catch the async thrown error before mocha + var listeners = process.listeners('uncaughtException'); + process.removeAllListeners('uncaughtException'); + process.once('uncaughtException', function onErr(err) { + listeners.forEach(function(listener) { + process.on('uncaughtException', listener); + }); + // can't throw errors in a uncaughtException handler, defer + setTimeout(checkErr, 0, err) + }) + + function checkErr(err) { + expect(err.message).to.match(/already called/); + done(); + } + + async.waterfall([ + function(callback){ + setTimeout(callback, 0, null, 'one', 'two'); + setTimeout(callback, 10, null, 'one', 'two'); + }, + function(arg1, arg2, callback){ + setTimeout(callback, 15, null, arg1, arg2, 'three'); + } + ], function () { + throw new Error('should not get here') + }); + }); + it('call in another context @nycinvalid @nodeonly', function(done) { var vm = require('vm'); var sandbox = { |